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.
Completed
Push — php72 ( 19e113...3c0860 )
by Joni
03:29
created

AESGCMKWAlgorithm   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 11
eloc 40
dl 0
loc 144
ccs 37
cts 37
cp 1
rs 10
c 0
b 0
f 0

6 Methods

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