-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
PBES2AESKW.php
123 lines (103 loc) · 3.89 KB
/
PBES2AESKW.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<?php
declare(strict_types=1);
namespace Jose\Component\Encryption\Algorithm\KeyEncryption;
use AESKW\A128KW;
use AESKW\A192KW;
use AESKW\A256KW;
use function in_array;
use InvalidArgumentException;
use function is_int;
use function is_string;
use Jose\Component\Core\JWK;
use ParagonIE\ConstantTime\Base64UrlSafe;
abstract class PBES2AESKW implements KeyWrapping
{
public function __construct(
private readonly int $salt_size = 64,
private readonly int $nb_count = 4096
) {
}
public function allowedKeyTypes(): array
{
return ['oct'];
}
public function wrapKey(JWK $key, string $cek, array $completeHeader, array &$additionalHeader): string
{
$password = $this->getKey($key);
$this->checkHeaderAlgorithm($completeHeader);
$wrapper = $this->getWrapper();
$hash_algorithm = $this->getHashAlgorithm();
$key_size = $this->getKeySize();
$salt = random_bytes($this->salt_size);
// We set header parameters
$additionalHeader['p2s'] = Base64UrlSafe::encodeUnpadded($salt);
$additionalHeader['p2c'] = $this->nb_count;
$derived_key = hash_pbkdf2(
$hash_algorithm,
$password,
$completeHeader['alg'] . "\x00" . $salt,
$this->nb_count,
$key_size,
true
);
return $wrapper::wrap($derived_key, $cek);
}
public function unwrapKey(JWK $key, string $encrypted_cek, array $completeHeader): string
{
$password = $this->getKey($key);
$this->checkHeaderAlgorithm($completeHeader);
$this->checkHeaderAdditionalParameters($completeHeader);
$wrapper = $this->getWrapper();
$hash_algorithm = $this->getHashAlgorithm();
$key_size = $this->getKeySize();
$salt = $completeHeader['alg'] . "\x00" . Base64UrlSafe::decode($completeHeader['p2s']);
$count = $completeHeader['p2c'];
$derived_key = hash_pbkdf2($hash_algorithm, $password, $salt, $count, $key_size, true);
return $wrapper::unwrap($derived_key, $encrypted_cek);
}
public function getKeyManagementMode(): string
{
return self::MODE_WRAP;
}
protected function getKey(JWK $key): string
{
if (! in_array($key->get('kty'), $this->allowedKeyTypes(), true)) {
throw new InvalidArgumentException('Wrong key type.');
}
if (! $key->has('k')) {
throw new InvalidArgumentException('The key parameter "k" is missing.');
}
$k = $key->get('k');
if (! is_string($k)) {
throw new InvalidArgumentException('The key parameter "k" is invalid.');
}
return Base64UrlSafe::decode($k);
}
protected function checkHeaderAlgorithm(array $header): void
{
if (! isset($header['alg'])) {
throw new InvalidArgumentException('The header parameter "alg" is missing.');
}
if (! is_string($header['alg'])) {
throw new InvalidArgumentException('The header parameter "alg" is not valid.');
}
}
protected function checkHeaderAdditionalParameters(array $header): void
{
if (! isset($header['p2s'])) {
throw new InvalidArgumentException('The header parameter "p2s" is missing.');
}
if (! is_string($header['p2s'])) {
throw new InvalidArgumentException('The header parameter "p2s" is not valid.');
}
if (! isset($header['p2c'])) {
throw new InvalidArgumentException('The header parameter "p2c" is missing.');
}
if (! is_int($header['p2c']) || $header['p2c'] <= 0) {
throw new InvalidArgumentException('The header parameter "p2c" is not valid.');
}
}
abstract protected function getWrapper(): A256KW|A128KW|A192KW;
abstract protected function getHashAlgorithm(): string;
abstract protected function getKeySize(): int;
}