PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / trunk
Transferito: WP Migration vtrunk
trunk 11.4.0 12.0.0 13.1.0 14.0.0 14.0.11 14.0.7 14.1.0 14.1.1 14.1.2 14.1.3 14.1.4
transferito / vendor / aws / aws-sdk-php / src / Crypto / Polyfill / AesGcm.php
transferito / vendor / aws / aws-sdk-php / src / Crypto / Polyfill Last commit date
AesGcm.php 11 months ago ByteArray.php 11 months ago Gmac.php 11 months ago Key.php 11 months ago NeedsTrait.php 11 months ago
AesGcm.php
229 lines
1 <?php
2 namespace Aws\Crypto\Polyfill;
3
4 use Aws\Exception\CryptoPolyfillException;
5 use InvalidArgumentException;
6 use RangeException;
7
8 /**
9 * Class AesGcm
10 *
11 * This provides a polyfill for AES-GCM encryption/decryption, with caveats:
12 *
13 * 1. Only 96-bit nonces are supported.
14 * 2. Only 128-bit authentication tags are supported. (i.e. non-truncated)
15 *
16 * Supports AES key sizes of 128-bit, 192-bit, and 256-bit.
17 *
18 * @package Aws\Crypto\Polyfill
19 */
20 class AesGcm
21 {
22 use NeedsTrait;
23
24 /** @var Key $aesKey */
25 private $aesKey;
26
27 /** @var int $keySize */
28 private $keySize;
29
30 /** @var int $blockSize */
31 protected $blockSize = 8192;
32
33 /**
34 * AesGcm constructor.
35 *
36 * @param Key $aesKey
37 * @param int $keySize
38 * @param int $blockSize
39 *
40 * @throws CryptoPolyfillException
41 * @throws InvalidArgumentException
42 * @throws RangeException
43 */
44 public function __construct(Key $aesKey, $keySize = 256, $blockSize = 8192)
45 {
46 /* Preconditions: */
47 self::needs(
48 \in_array($keySize, [128, 192, 256], true),
49 "Key size must be 128, 192, or 256 bits; {$keySize} given",
50 InvalidArgumentException::class
51 );
52 self::needs(
53 \is_int($blockSize) && $blockSize > 0 && $blockSize <= PHP_INT_MAX,
54 'Block size must be a positive integer.',
55 RangeException::class
56 );
57 self::needs(
58 $aesKey->length() << 3 === $keySize,
59 'Incorrect key size; expected ' . $keySize . ' bits, got ' . ($aesKey->length() << 3) . ' bits.'
60 );
61 $this->aesKey = $aesKey;
62 $this->keySize = $keySize;
63 }
64
65 /**
66 * Encryption interface for AES-GCM
67 *
68 * @param string $plaintext Message to be encrypted
69 * @param string $nonce Number to be used ONCE
70 * @param Key $key AES Key
71 * @param string $aad Additional authenticated data
72 * @param string &$tag Reference to variable to hold tag
73 * @param int $keySize Key size (bits)
74 * @param int $blockSize Block size (bytes) -- How much memory to buffer
75 * @return string
76 * @throws InvalidArgumentException
77 */
78 public static function encrypt(
79 $plaintext,
80 $nonce,
81 Key $key,
82 $aad,
83 &$tag,
84 $keySize = 256,
85 $blockSize = 8192
86 ) {
87 self::needs(
88 self::strlen($nonce) === 12,
89 'Nonce must be exactly 12 bytes',
90 InvalidArgumentException::class
91 );
92
93 $encryptor = new AesGcm($key, $keySize, $blockSize);
94 list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad);
95
96 $ciphertext = \openssl_encrypt(
97 $plaintext,
98 "aes-{$encryptor->keySize}-ctr",
99 $key->get(),
100 OPENSSL_NO_PADDING | OPENSSL_RAW_DATA,
101 $nonce . "\x00\x00\x00\x02"
102 );
103
104 /* Calculate auth tag in a streaming fashion to minimize memory usage: */
105 $ciphertextLength = self::strlen($ciphertext);
106 for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) {
107 $cBlock = new ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));
108 $gmac->update($cBlock);
109 }
110 $tag = $gmac->finish($aadLength, $ciphertextLength)->toString();
111 return $ciphertext;
112 }
113
114 /**
115 * Decryption interface for AES-GCM
116 *
117 * @param string $ciphertext Ciphertext to decrypt
118 * @param string $nonce Number to be used ONCE
119 * @param Key $key AES key
120 * @param string $aad Additional authenticated data
121 * @param string $tag Authentication tag
122 * @param int $keySize Key size (bits)
123 * @param int $blockSize Block size (bytes) -- How much memory to buffer
124 * @return string Plaintext
125 *
126 * @throws CryptoPolyfillException
127 * @throws InvalidArgumentException
128 */
129 public static function decrypt(
130 $ciphertext,
131 $nonce,
132 Key $key,
133 $aad,
134 &$tag,
135 $keySize = 256,
136 $blockSize = 8192
137 ) {
138 /* Precondition: */
139 self::needs(
140 self::strlen($nonce) === 12,
141 'Nonce must be exactly 12 bytes',
142 InvalidArgumentException::class
143 );
144
145 $encryptor = new AesGcm($key, $keySize, $blockSize);
146 list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad);
147
148 /* Calculate auth tag in a streaming fashion to minimize memory usage: */
149 $ciphertextLength = self::strlen($ciphertext);
150 for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) {
151 $cBlock = new ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));
152 $gmac->update($cBlock);
153 }
154
155 /* Validate auth tag in constant-time: */
156 $calc = $gmac->finish($aadLength, $ciphertextLength);
157 $expected = new ByteArray($tag);
158 self::needs($calc->equals($expected), 'Invalid authentication tag');
159
160 /* Return plaintext if auth tag check succeeded: */
161 return \openssl_decrypt(
162 $ciphertext,
163 "aes-{$encryptor->keySize}-ctr",
164 $key->get(),
165 OPENSSL_NO_PADDING | OPENSSL_RAW_DATA,
166 $nonce . "\x00\x00\x00\x02"
167 );
168 }
169
170 /**
171 * Initialize a Gmac object with the nonce and this object's key.
172 *
173 * @param string $nonce Must be exactly 12 bytes long.
174 * @param string|null $aad
175 * @return array
176 */
177 protected function gmacInit($nonce, $aad = null)
178 {
179 $gmac = new Gmac(
180 $this->aesKey,
181 $nonce . "\x00\x00\x00\x01",
182 $this->keySize
183 );
184 $aadBlock = new ByteArray($aad);
185 $aadLength = $aadBlock->count();
186 $gmac->update($aadBlock);
187 $gmac->flush();
188 return [$aadLength, $gmac];
189 }
190
191 /**
192 * Calculate the length of a string.
193 *
194 * Uses the appropriate PHP function without being brittle to
195 * mbstring.func_overload.
196 *
197 * @param string $string
198 * @return int
199 */
200 protected static function strlen($string)
201 {
202 if (\is_callable('\\mb_strlen')) {
203 return (int) \mb_strlen($string, '8bit');
204 }
205 return (int) \strlen($string);
206 }
207
208 /**
209 * Return a substring of the provided string.
210 *
211 * Uses the appropriate PHP function without being brittle to
212 * mbstring.func_overload.
213 *
214 * @param string $string
215 * @param int $offset
216 * @param int|null $length
217 * @return string
218 */
219 protected static function substr($string, $offset = 0, $length = null)
220 {
221 if (\is_callable('\\mb_substr')) {
222 return \mb_substr($string, $offset, $length, '8bit');
223 } elseif (!\is_null($length)) {
224 return \substr($string, $offset, $length);
225 }
226 return \substr($string, $offset);
227 }
228 }
229