GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

RSAESKeyAlgorithm   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 19
eloc 55
dl 0
loc 178
ccs 56
cts 56
cp 1
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A fromPrivateKey() 0 3 1
A _getLastOpenSSLError() 0 7 2
A hasPrivateKey() 0 3 1
A _decryptKey() 0 17 3
A fromPublicKey() 0 3 1
A _encryptKey() 0 17 3
A privateKey() 0 6 2
A publicKey() 0 3 1
A headerParameters() 0 4 1
A fromJWK() 0 11 3
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\JWX\JWE\KeyAlgorithm;
6
7
use Sop\JWX\JWA\JWA;
8
use Sop\JWX\JWE\KeyAlgorithm\Feature\RandomCEK;
9
use Sop\JWX\JWE\KeyManagementAlgorithm;
10
use Sop\JWX\JWK\JWK;
11
use Sop\JWX\JWK\RSA\RSAPrivateKeyJWK;
12
use Sop\JWX\JWK\RSA\RSAPublicKeyJWK;
13
use Sop\JWX\JWT\Header\Header;
14
use Sop\JWX\JWT\Parameter\AlgorithmParameter;
15
16
/**
17
 * Base class for algorithms implementing RSA based key encryption.
18
 *
19
 * @see https://tools.ietf.org/html/rfc7518#section-4.2
20
 * @see https://tools.ietf.org/html/rfc7518#section-4.3
21
 */
22
abstract class RSAESKeyAlgorithm extends KeyManagementAlgorithm
23
{
24
    use RandomCEK;
25
26
    /**
27
     * Mapping from algorithm name to class name.
28
     *
29
     * @internal
30
     *
31
     * @var array
32
     */
33
    public const MAP_ALGO_TO_CLASS = [
34
        JWA::ALGO_RSA1_5 => RSAESPKCS1Algorithm::class,
35
        JWA::ALGO_RSA_OAEP => RSAESOAEPAlgorithm::class,
36
    ];
37
38
    /**
39
     * Public key.
40
     *
41
     * @var RSAPublicKeyJWK
42
     */
43
    protected $_publicKey;
44
45
    /**
46
     * Private key.
47
     *
48
     * @var null|RSAPrivateKeyJWK
49
     */
50
    protected $_privateKey;
51
52
    /**
53
     * Constructor.
54
     *
55
     * Use `fromPublicKey` or `fromPrivateKey` instead!
56
     *
57
     * @param RSAPublicKeyJWK  $pub_key  RSA public key
58
     * @param RSAPrivateKeyJWK $priv_key Optional RSA private key
59
     */
60 18
    protected function __construct(RSAPublicKeyJWK $pub_key,
61
        ?RSAPrivateKeyJWK $priv_key = null)
62
    {
63 18
        $this->_publicKey = $pub_key;
64 18
        $this->_privateKey = $priv_key;
65 18
    }
66
67
    /**
68
     * Initialize from JWK.
69
     *
70
     * @throws \UnexpectedValueException
71
     *
72
     * @return self
73
     */
74 3
    public static function fromJWK(JWK $jwk, Header $header): KeyManagementAlgorithm
75
    {
76 3
        $alg = JWA::deriveAlgorithmName($header, $jwk);
77 3
        if (!array_key_exists($alg, self::MAP_ALGO_TO_CLASS)) {
78 1
            throw new \UnexpectedValueException("Unsupported algorithm '{$alg}'.");
79
        }
80 2
        $cls = self::MAP_ALGO_TO_CLASS[$alg];
81 2
        if ($jwk->has(...RSAPrivateKeyJWK::MANAGED_PARAMS)) {
82 1
            return $cls::fromPrivateKey(RSAPrivateKeyJWK::fromJWK($jwk));
83
        }
84 1
        return $cls::fromPublicKey(RSAPublicKeyJWK::fromJWK($jwk));
85
    }
86
87
    /**
88
     * Initialize from a public key.
89
     */
90 3
    public static function fromPublicKey(RSAPublicKeyJWK $jwk): self
91
    {
92 3
        return new static($jwk);
93
    }
94
95
    /**
96
     * Initialize from a private key.
97
     */
98 15
    public static function fromPrivateKey(RSAPrivateKeyJWK $jwk): self
99
    {
100 15
        return new static($jwk->publicKey(), $jwk);
101
    }
102
103
    /**
104
     * Get the public key.
105
     */
106 11
    public function publicKey(): RSAPublicKeyJWK
107
    {
108 11
        return $this->_publicKey;
109
    }
110
111
    /**
112
     * Check whether the private key is present.
113
     */
114 13
    public function hasPrivateKey(): bool
115
    {
116 13
        return isset($this->_privateKey);
117
    }
118
119
    /**
120
     * Get the private key.
121
     *
122
     * @throws \LogicException
123
     */
124 12
    public function privateKey(): RSAPrivateKeyJWK
125
    {
126 12
        if (!$this->hasPrivateKey()) {
127 1
            throw new \LogicException('Private key not set.');
128
        }
129 11
        return $this->_privateKey;
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 3
    public function headerParameters(): array
136
    {
137 3
        return array_merge(parent::headerParameters(),
138 3
            [AlgorithmParameter::fromAlgorithm($this)]);
139
    }
140
141
    /**
142
     * Get the padding scheme.
143
     */
144
    abstract protected function _paddingScheme(): int;
145
146
    /**
147
     * {@inheritdoc}
148
     */
149 10
    protected function _encryptKey(string $key, Header &$header): string
150
    {
151 10
        $pubkey = openssl_pkey_get_public(
152 10
            $this->publicKey()->toPEM()->string());
153 10
        if (false === $pubkey) {
154 1
            throw new \RuntimeException(
155
                'openssl_pkey_get_public() failed: ' .
156 1
                     $this->_getLastOpenSSLError());
157
        }
158 9
        $result = openssl_public_encrypt($key, $crypted, $pubkey,
159 9
            $this->_paddingScheme());
160 9
        if (!$result) {
161 1
            throw new \RuntimeException(
162
                'openssl_public_encrypt() failed: ' .
163 1
                     $this->_getLastOpenSSLError());
164
        }
165 8
        return $crypted;
166
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171 10
    protected function _decryptKey(string $ciphertext, Header $header): string
172
    {
173 10
        $privkey = openssl_pkey_get_private(
174 10
            $this->privateKey()->toPEM()->string());
175 10
        if (false === $privkey) {
176 1
            throw new \RuntimeException(
177
                'openssl_pkey_get_private() failed: ' .
178 1
                     $this->_getLastOpenSSLError());
179
        }
180 9
        $result = openssl_private_decrypt($ciphertext, $cek, $privkey,
181 9
            $this->_paddingScheme());
182 9
        if (!$result) {
183 1
            throw new \RuntimeException(
184
                'openssl_private_decrypt() failed: ' .
185 1
                     $this->_getLastOpenSSLError());
186
        }
187 8
        return $cek;
188
    }
189
190
    /**
191
     * Get last OpenSSL error message.
192
     */
193 4
    protected function _getLastOpenSSLError(): ?string
194
    {
195 4
        $msg = null;
196 4
        while (false !== ($err = openssl_error_string())) {
197 4
            $msg = $err;
198
        }
199 4
        return $msg;
200
    }
201
}
202