1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the tmilos/jose-jwt package. |
5
|
|
|
* |
6
|
|
|
* (c) Milos Tomic <[email protected]> |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the MIT license that is bundled |
9
|
|
|
* with this source code in the file LICENSE. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Tmilos\JoseJwt\Jwe; |
13
|
|
|
|
14
|
|
|
use Tmilos\JoseJwt\Error\IntegrityException; |
15
|
|
|
use Tmilos\JoseJwt\Error\JoseJwtException; |
16
|
|
|
use Tmilos\JoseJwt\Jws\JwsAlgorithm; |
17
|
|
|
use Tmilos\JoseJwt\Random\RandomGenerator; |
18
|
|
|
use Tmilos\JoseJwt\Util\StringUtils; |
19
|
|
|
|
20
|
|
|
class AesCbcHmacEncryption implements JweEncryption |
21
|
|
|
{ |
22
|
|
|
/** @var JwsAlgorithm */ |
23
|
|
|
private $hashAlgorithm; |
24
|
|
|
|
25
|
|
|
/** @var int */ |
26
|
|
|
private $keySize; |
27
|
|
|
|
28
|
|
|
/** @var RandomGenerator */ |
29
|
|
|
private $randomGenerator; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @param int $keySize |
33
|
|
|
* @param JwsAlgorithm $hashAlgorithm |
34
|
|
|
* @param RandomGenerator $randomGenerator |
35
|
|
|
*/ |
36
|
|
|
public function __construct($keySize, JwsAlgorithm $hashAlgorithm, RandomGenerator $randomGenerator) |
37
|
|
|
{ |
38
|
|
|
$this->keySize = $keySize; |
39
|
|
|
$this->hashAlgorithm = $hashAlgorithm; |
40
|
|
|
$this->randomGenerator = $randomGenerator; |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @return int |
45
|
|
|
*/ |
46
|
|
|
public function getKeySize() |
47
|
|
|
{ |
48
|
|
|
return $this->keySize; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @param string $aad |
53
|
|
|
* @param string $plainText |
54
|
|
|
* @param string|resource $cek |
55
|
|
|
* |
56
|
|
|
* @return array [iv, cipherText, authTag] |
57
|
|
|
*/ |
58
|
|
|
public function encrypt($aad, $plainText, $cek) |
59
|
|
|
{ |
60
|
|
|
$cekLen = StringUtils::length($cek); |
|
|
|
|
61
|
|
View Code Duplication |
if ($cekLen * 8 != $this->keySize) { |
|
|
|
|
62
|
|
|
throw new JoseJwtException(sprintf('AES-CBC with HMAC algorithm expected key of size %s bits, but was given %s bits', $this->keySize, $cekLen * 8)); |
63
|
|
|
} |
64
|
|
|
if ($cekLen % 2 != 0) { |
65
|
|
|
throw new JoseJwtException('AES-CBC with HMAC encryption expected key of even number size'); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
$hmacKey = StringUtils::substring($cek, 0, $cekLen / 2); |
|
|
|
|
69
|
|
|
$aesKey = StringUtils::substring($cek, $cekLen / 2, $cekLen / 2); |
|
|
|
|
70
|
|
|
|
71
|
|
|
$method = sprintf('AES-%d-CBC', $this->keySize / 2); |
72
|
|
|
$ivLen = openssl_cipher_iv_length($method); |
73
|
|
|
$iv = $this->randomGenerator->get($ivLen); |
74
|
|
|
$cipherText = openssl_encrypt($plainText, $method, $aesKey, true, $iv); |
75
|
|
|
|
76
|
|
|
$authTag = $this->computeAuthTag($aad, $iv, $cipherText, $hmacKey); |
77
|
|
|
|
78
|
|
|
return [$iv, $cipherText, $authTag]; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @param string $aad |
83
|
|
|
* @param string|resource $cek |
84
|
|
|
* @param string $iv |
85
|
|
|
* @param string $cipherText |
86
|
|
|
* @param string $authTag |
87
|
|
|
* |
88
|
|
|
* @return string |
89
|
|
|
*/ |
90
|
|
|
public function decrypt($aad, $cek, $iv, $cipherText, $authTag) |
91
|
|
|
{ |
92
|
|
|
$cekLen = StringUtils::length($cek); |
|
|
|
|
93
|
|
View Code Duplication |
if ($cekLen * 8 != $this->keySize) { |
|
|
|
|
94
|
|
|
throw new JoseJwtException(sprintf('AES-CBC with HMAC algorithm expected key of size %s bits, but was given %s bits', $this->keySize, $cekLen * 8)); |
95
|
|
|
} |
96
|
|
|
if ($cekLen % 2 != 0) { |
97
|
|
|
throw new JoseJwtException('AES-CBC with HMAC encryption expected key of even number size'); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
$hmacKey = StringUtils::substring($cek, 0, $cekLen / 2); |
|
|
|
|
101
|
|
|
$aesKey = StringUtils::substring($cek, $cekLen / 2); |
|
|
|
|
102
|
|
|
|
103
|
|
|
$expectedAuthTag = $this->computeAuthTag($aad, $iv, $cipherText, $hmacKey); |
104
|
|
|
if (false === StringUtils::equals($expectedAuthTag, $authTag)) { |
105
|
|
|
throw new IntegrityException('Authentication tag does not match'); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
$method = sprintf('AES-%d-CBC', $this->keySize / 2); |
109
|
|
|
$plainText = openssl_decrypt($cipherText, $method, $aesKey, true, $iv); |
110
|
|
|
|
111
|
|
|
return $plainText; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* @param $aad |
116
|
|
|
* @param $iv |
117
|
|
|
* @param $cipherText |
118
|
|
|
* @param $hmacKey |
119
|
|
|
* |
120
|
|
|
* @return string |
121
|
|
|
*/ |
122
|
|
|
private function computeAuthTag($aad, $iv, $cipherText, $hmacKey) |
123
|
|
|
{ |
124
|
|
|
$aadLen = StringUtils::length($aad); |
125
|
|
|
$max32bit = 2147483647; |
126
|
|
|
$hmacInput = implode('', [ |
127
|
|
|
$aad, |
128
|
|
|
$iv, |
129
|
|
|
$cipherText, |
130
|
|
|
pack('N2', ($aadLen / $max32bit) * 8, ($aadLen % $max32bit) * 8), |
131
|
|
|
]); |
132
|
|
|
$authTag = $this->hashAlgorithm->sign($hmacInput, $hmacKey); |
133
|
|
|
$authTagLen = StringUtils::length($authTag); |
134
|
|
|
$authTag = StringUtils::substring($authTag, 0, $authTagLen / 2); |
135
|
|
|
|
136
|
|
|
return $authTag; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.