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.
Passed
Branch php72 (880eb0)
by Joni
05:58
created

AESGCMKWAlgorithm   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 12
eloc 42
dl 0
loc 162
ccs 40
cts 40
cp 1
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 3
A _getGCM() 0 3 1
A _encryptKey() 0 8 1
A fromJWK() 0 13 3
A headerParameters() 0 5 1
A fromKey() 0 4 1
A _decryptKey() 0 9 2
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\JWX\JWE\KeyAlgorithm;
6
7
use Sop\GCM\Cipher\Cipher;
8
use Sop\GCM\GCM;
9
use Sop\JWX\JWA\JWA;
10
use Sop\JWX\JWE\KeyAlgorithm\Feature\RandomCEK;
11
use Sop\JWX\JWE\KeyManagementAlgorithm;
12
use Sop\JWX\JWK\JWK;
13
use Sop\JWX\JWK\Symmetric\SymmetricKeyJWK;
14
use Sop\JWX\JWT\Header\Header;
15
use Sop\JWX\JWT\Parameter\AlgorithmParameter;
16
use Sop\JWX\JWT\Parameter\AuthenticationTagParameter;
17
use Sop\JWX\JWT\Parameter\InitializationVectorParameter;
18
19
/**
20
 * Base class for AES GCM key encryption algorithms.
21
 *
22
 * @see https://tools.ietf.org/html/rfc7518#section-4.7
23
 */
24
abstract class AESGCMKWAlgorithm extends KeyManagementAlgorithm
25
{
26
    use RandomCEK;
27
28
    /**
29
     * Mapping from algorithm name to class name.
30
     *
31
     * @internal
32
     *
33
     * @var array
34
     */
35
    const MAP_ALGO_TO_CLASS = [
36
        JWA::ALGO_A128GCMKW => A128GCMKWAlgorithm::class,
37
        JWA::ALGO_A192GCMKW => A192GCMKWAlgorithm::class,
38
        JWA::ALGO_A256GCMKW => A256GCMKWAlgorithm::class,
39
    ];
40
41
    /**
42
     * Required IV size in bytes.
43
     *
44
     * @var int
45
     */
46
    const IV_SIZE = 12;
47
48
    /**
49
     * Authentication tag size in bytes.
50
     *
51
     * @var int
52
     */
53
    const AUTH_TAG_SIZE = 16;
54
55
    /**
56
     * Key encryption key.
57
     *
58
     * @var string
59
     */
60
    protected $_kek;
61
62
    /**
63
     * Initialization vector.
64
     *
65
     * @var string
66
     */
67
    protected $_iv;
68
69
    /**
70
     * Constructor.
71
     *
72
     * @param string $kek Key encryption key
73
     * @param string $iv  Initialization vector
74
     */
75 14
    public function __construct(string $kek, string $iv)
76
    {
77 14
        if (strlen($kek) !== $this->_keySize()) {
78 3
            throw new \LengthException('Invalid key size.');
79
        }
80 11
        if (self::IV_SIZE !== strlen($iv)) {
81 1
            throw new \LengthException('Initialization vector must be 96 bits.');
82
        }
83 10
        $this->_kek = $kek;
84 10
        $this->_iv = $iv;
85 10
    }
86
87
    /**
88
     * Initialize from JWK.
89
     *
90
     * @param JWK    $jwk
91
     * @param Header $header
92
     *
93
     * @throws \UnexpectedValueException
94
     *
95
     * @return self
96
     */
97 7
    public static function fromJWK(JWK $jwk, Header $header): KeyManagementAlgorithm
98
    {
99 7
        $jwk = SymmetricKeyJWK::fromJWK($jwk);
100 7
        if (!$header->hasInitializationVector()) {
101 1
            throw new \UnexpectedValueException('No initialization vector.');
102
        }
103 6
        $iv = $header->initializationVector()->initializationVector();
104 6
        $alg = JWA::deriveAlgorithmName($header, $jwk);
105 5
        if (!array_key_exists($alg, self::MAP_ALGO_TO_CLASS)) {
106 1
            throw new \UnexpectedValueException("Unsupported algorithm '{$alg}'.");
107
        }
108 4
        $cls = self::MAP_ALGO_TO_CLASS[$alg];
109 4
        return new $cls($jwk->key(), $iv);
110
    }
111
112
    /**
113
     * Initialize from key encryption key with random IV.
114
     *
115
     * Key size must match the underlying cipher.
116
     *
117
     * @param string $key Key encryption key
118
     *
119
     * @return self
120
     */
121 1
    public static function fromKey(string $key): self
122
    {
123 1
        $iv = openssl_random_pseudo_bytes(self::IV_SIZE);
124 1
        return new static($key, $iv);
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130 2
    public function headerParameters(): array
131
    {
132 2
        return array_merge(parent::headerParameters(),
133 2
            [AlgorithmParameter::fromAlgorithm($this),
134 2
                InitializationVectorParameter::fromString($this->_iv), ]);
135
    }
136
137
    /**
138
     * Get GCM Cipher instance.
139
     *
140
     * @return Cipher
141
     */
142
    abstract protected function _getGCMCipher(): Cipher;
143
144
    /**
145
     * Get the required key size.
146
     *
147
     * @return int
148
     */
149
    abstract protected function _keySize(): int;
150
151
    /**
152
     * Get GCM instance.
153
     *
154
     * @return GCM
155
     */
156 9
    final protected function _getGCM(): GCM
157
    {
158 9
        return new GCM($this->_getGCMCipher(), self::AUTH_TAG_SIZE);
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 5
    protected function _encryptKey(string $key, Header &$header): string
165
    {
166 5
        [$ciphertext, $auth_tag] = $this->_getGCM()
167 5
            ->encrypt($key, '', $this->_kek, $this->_iv);
168
        // insert authentication tag to the header
169 5
        $header = $header->withParameters(
170 5
            AuthenticationTagParameter::fromString($auth_tag));
171 5
        return $ciphertext;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 5
    protected function _decryptKey(string $ciphertext, Header $header): string
178
    {
179 5
        if (!$header->hasAuthenticationTag()) {
180 1
            throw new \RuntimeException(
181 1
                "Header doesn't contain authentication tag.");
182
        }
183 4
        $auth_tag = $header->authenticationTag()->authenticationTag();
184 4
        return $this->_getGCM()
185 4
            ->decrypt($ciphertext, $auth_tag, '', $this->_kek, $this->_iv);
186
    }
187
}
188