Skip to content

Commit 8f42dc0

Browse files
authored
Validate MAC per key (#51063)
* Ensure mac is validated against keys individually * lint
1 parent bf65004 commit 8f42dc0

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

src/Illuminate/Encryption/Encrypter.php

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,19 @@ public function decrypt($payload, $unserialize = true)
163163
$tag = empty($payload['tag']) ? null : base64_decode($payload['tag'])
164164
);
165165

166+
$foundValidMac = false;
167+
166168
// Here we will decrypt the value. If we are able to successfully decrypt it
167169
// we will then unserialize it and return it out to the caller. If we are
168170
// unable to decrypt this value we will throw out an exception message.
169171
foreach ($this->getAllKeys() as $key) {
172+
if (
173+
$this->shouldValidateMac() &&
174+
! ($foundValidMac = $foundValidMac || $this->validMacForKey($payload, $key))
175+
) {
176+
continue;
177+
}
178+
170179
$decrypted = \openssl_decrypt(
171180
$payload['value'], strtolower($this->cipher), $key, 0, $iv, $tag ?? ''
172181
);
@@ -176,7 +185,11 @@ public function decrypt($payload, $unserialize = true)
176185
}
177186
}
178187

179-
if ($decrypted === false) {
188+
if ($this->shouldValidateMac() && ! $foundValidMac) {
189+
throw new DecryptException('The MAC is invalid.');
190+
}
191+
192+
if (($decrypted ?? false) === false) {
180193
throw new DecryptException('Could not decrypt the data.');
181194
}
182195

@@ -232,10 +245,6 @@ protected function getJsonPayload($payload)
232245
throw new DecryptException('The payload is invalid.');
233246
}
234247

235-
if (! self::$supportedCiphers[strtolower($this->cipher)]['aead'] && ! $this->validMac($payload)) {
236-
throw new DecryptException('The MAC is invalid.');
237-
}
238-
239248
return $payload;
240249
}
241250

@@ -265,24 +274,28 @@ protected function validPayload($payload)
265274
}
266275

267276
/**
268-
* Determine if the MAC for the given payload is valid.
277+
* Determine if the MAC for the given payload is valid for the primary key.
269278
*
270279
* @param array $payload
271280
* @return bool
272281
*/
273282
protected function validMac(array $payload)
274283
{
275-
foreach ($this->getAllKeys() as $key) {
276-
$valid = hash_equals(
277-
$this->hash($payload['iv'], $payload['value'], $key), $payload['mac']
278-
);
279-
280-
if ($valid === true) {
281-
return true;
282-
}
283-
}
284+
return $this->validMacForKey($payload, $this->key);
285+
}
284286

285-
return false;
287+
/**
288+
* Determine if the MAC is valid for the given payload and key.
289+
*
290+
* @param array $payload
291+
* @param string $key
292+
* @return bool
293+
*/
294+
protected function validMacForKey($payload, $key)
295+
{
296+
return hash_equals(
297+
$this->hash($payload['iv'], $payload['value'], $key), $payload['mac']
298+
);
286299
}
287300

288301
/**
@@ -302,6 +315,16 @@ protected function ensureTagIsValid($tag)
302315
}
303316
}
304317

318+
/**
319+
* Determine if we should validate the MAC while decrypting.
320+
*
321+
* @return bool
322+
*/
323+
protected function shouldValidateMac()
324+
{
325+
return ! self::$supportedCiphers[strtolower($this->cipher)]['aead'];
326+
}
327+
305328
/**
306329
* Get the encryption key that the encrypter is currently using.
307330
*

tests/Encryption/EncrypterTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ public function testRawStringEncryptionWithPreviousKeys()
3838
$this->assertSame('foo', $decrypted);
3939
}
4040

41+
public function testItValidatesMacOnPerKeyBasis()
42+
{
43+
// Payload created with (key: str_repeat('b', 16)) but will
44+
// "successfully" decrypt with (key: str_repeat('a', 16)), however it
45+
// outputs a random binary string as it is not the correct key.
46+
$encrypted = 'eyJpdiI6Ilg0dFM5TVRibEFqZW54c3lQdWJoVVE9PSIsInZhbHVlIjoiRGJpa2p2ZHI3eUs0dUtRakJneUhUUT09IiwibWFjIjoiMjBjZWYxODdhNThhOTk4MTk1NTc0YTE1MDgzODU1OWE0ZmQ4MDc5ZjMxYThkOGM1ZmM1MzlmYzBkYTBjMWI1ZiIsInRhZyI6IiJ9';
47+
48+
$new = new Encrypter(str_repeat('a', 16));
49+
$new->previousKeys([str_repeat('b', 16)]);
50+
$this->assertSame('foo', $new->decryptString($encrypted));
51+
}
52+
4153
public function testEncryptionUsingBase64EncodedKey()
4254
{
4355
$e = new Encrypter(random_bytes(16));

0 commit comments

Comments
 (0)