Skip to content

Commit d65b3d2

Browse files
committed
Fixed decrypt method if a non hash is informed
1 parent 30a1c56 commit d65b3d2

File tree

7 files changed

+111
-54
lines changed

7 files changed

+111
-54
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Released Notes
22

3+
## v3.1.4 - (2024-09-12)
4+
5+
### Fixed
6+
7+
- Fixed `decrypt` method if a non hash is informed
8+
9+
-----------------------------------------------------------
10+
311
## v3.1.3 - (2024-08-25)
412

513
### Added

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"paragonie/sodium_compat": "^1.20"
1313
},
1414
"require-dev": {
15-
"phpunit/phpunit": "^10"
15+
"phpunit/phpunit": "^11"
1616
},
1717
"autoload": {
1818
"psr-4": {

src/Encrypt/Adapter/OpenSslEncryption.php

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,31 +40,40 @@ public function __construct(string $key)
4040
}
4141

4242
/**
43-
* Encrypt the message.
44-
*
45-
* @param mixed $data => data to be encrypted
46-
*
47-
* @return mixed
43+
* {@inheritDoc}
4844
*/
4945
public function encrypt(mixed $data): mixed
5046
{
5147
return base64_encode(
52-
openssl_encrypt($data, $this->cipher, $this->key, 0, $this->iv) . '&&' . bin2hex($this->iv)
48+
openssl_encrypt(
49+
$data,
50+
$this->cipher,
51+
$this->key,
52+
0,
53+
$this->iv
54+
) . '&&' . bin2hex($this->iv)
5355
);
5456
}
5557

5658
/**
57-
* Decrypt the message.
58-
*
59-
* @param mixed $token => encrypted token
60-
61-
* @return string|bool
59+
* {@inheritDoc}
6260
*/
6361
public function decrypt(mixed $token): string|bool
6462
{
6563
$token = base64_decode($token);
66-
list($token, $this->iv) = explode('&&', $token);
67-
return openssl_decrypt($token, $this->cipher, $this->key, 0, hex2bin($this->iv));
64+
$explode_value = explode('&&', $token);
65+
if (!array_key_exists(1, $explode_value)) return false;
66+
67+
list($token, $this->iv) = $explode_value;
68+
if (!$this->isHex($this->iv)) return false;
69+
70+
return openssl_decrypt(
71+
$token,
72+
$this->cipher,
73+
$this->key,
74+
0,
75+
hex2bin($this->iv)
76+
);
6877
}
6978

7079
/**
@@ -78,4 +87,16 @@ protected function ivBytes(string $method): int
7887
{
7988
return openssl_cipher_iv_length($method);
8089
}
90+
91+
/**
92+
* Check if String Is a Hexadecimal Value
93+
*
94+
* @param string $str
95+
*
96+
* @return bool
97+
*/
98+
private function isHex(string $str): bool
99+
{
100+
return preg_match('/^(?:0x)?[a-f0-9]{1,}$/i', $str);
101+
}
81102
}

src/Encrypt/Adapter/SodiumEncryption.php

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,40 +28,53 @@ public function __construct(string $key)
2828
}
2929

3030
/**
31-
* Encrypt the message.
32-
*
33-
* @param string $data data to be encrypted
34-
*
35-
* @return mixed
31+
* {@inheritDoc}
3632
*/
3733
public function encrypt(mixed $data): mixed
3834
{
3935
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
40-
$token = base64_encode($nonce . sodium_crypto_secretbox($data, $nonce, $this->key) . '&&' . $this->key);
41-
return $token;
36+
return base64_encode(
37+
$nonce . sodium_crypto_secretbox(
38+
$data,
39+
$nonce,
40+
$this->key
41+
) . '&&' . $this->key
42+
);
4243
}
4344

4445
/**
45-
* Decrypt the message.
46-
*
47-
* @param mixed $token encrypted token
48-
49-
* @return mixed
46+
* {@inheritDoc}
5047
*/
5148
public function decrypt(mixed $token): mixed
5249
{
5350
$decoded = base64_decode($token);
54-
list($decoded, $this->key) = explode('&&', $decoded);
51+
$explode_value = explode('&&', $decoded);
52+
if (!array_key_exists(1, $explode_value)) return false;
53+
54+
list($decoded, $this->key) = $explode_value;
5555
if ($decoded === false) throw new \Exception('The decoding failed');
5656

5757
if (
58-
mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)
58+
mb_strlen($decoded, '8bit') <
59+
(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)
5960
) {
6061
throw new \Exception('The token was truncated');
6162
}
6263

63-
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
64-
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
64+
$nonce = mb_substr(
65+
$decoded,
66+
0,
67+
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES,
68+
'8bit'
69+
);
70+
71+
$ciphertext = mb_substr(
72+
$decoded,
73+
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES,
74+
null,
75+
'8bit'
76+
);
77+
6578
$plain = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key);
6679
if ($plain === false) throw new \Exception('The message was tampered with in transit');
6780
return $plain;

src/SecurePassword.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function __construct(
3939
($this->config["algo"] instanceof AlgorithmEnum) ?
4040
$algo_config = $this->config["algo"]->value :
4141
$algo_config = $this->config["algo"];
42-
42+
4343
$this->options = $this->config;
4444
$this->algo = ($algo_config == "default") ? PASSWORD_DEFAULT : $algo_config;
4545
$this->setPepper();
@@ -56,7 +56,13 @@ public function createHash(#[SensitiveParameter] string $password): SecurePasswo
5656
{
5757
$this->password = $password;
5858
$pwd_peppered = $this->passwordPeppered($this->password);
59-
$this->pwd_hashed = password_hash($pwd_peppered, $this->algo, $this->options);
59+
60+
$this->pwd_hashed = password_hash(
61+
$pwd_peppered,
62+
$this->algo,
63+
$this->options
64+
);
65+
6066
return $this;
6167
}
6268

@@ -142,7 +148,9 @@ public function needsRehash(
142148
#[SensitiveParameter] string $password,
143149
#[SensitiveParameter] string $hash
144150
): string|false {
145-
return (password_needs_rehash($hash, $this->algo)) ? $this->createHash($password)->getHash() : false;
151+
return (password_needs_rehash($hash, $this->algo)) ?
152+
$this->createHash($password)->getHash() :
153+
false;
146154
}
147155

148156
/**

tests/EncryptionTest.php

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace Lablnet\Tests;
44

5-
use SecurePassword\Encrypt\Adapter\{OpenSslEncryption, SodiumEncryption};
6-
use SecurePassword\Encrypt\Encryption;
75
use PHPUnit\Framework\TestCase;
6+
use SecurePassword\Encrypt\Encryption;
7+
use SecurePassword\Encrypt\Adapter\{OpenSslEncryption, SodiumEncryption};
88

99
class EncryptionTest extends TestCase
1010
{
@@ -32,15 +32,13 @@ public function testEncryptAndDecryptWithOpenSsl()
3232

3333
public function testEncryptAndDecryptWithSodium()
3434
{
35-
if (extension_loaded('sodium')) {
36-
$encryption = new Encryption(new SodiumEncryption('euyq74tjfdskjFDSGq74'));
37-
$encryptedString = $encryption->encrypt('plain-text');
38-
$decryptedString = $encryption->decrypt($encryptedString);
39-
40-
$this->assertStringEndsNotWith('==', $encryptedString);
41-
$this->assertSame(112, strlen($encryptedString));
42-
$this->assertSame('plain-text', $decryptedString);
43-
}
35+
$encryption = new Encryption(new SodiumEncryption('euyq74tjfdskjFDSGq74'));
36+
$encryptedString = $encryption->encrypt('plain-text');
37+
$decryptedString = $encryption->decrypt($encryptedString);
38+
39+
$this->assertStringEndsNotWith('==', $encryptedString);
40+
$this->assertSame(112, strlen($encryptedString));
41+
$this->assertSame('plain-text', $decryptedString);
4442
}
4543

4644
public function testOpenSslEncrpytionEncryptOnEmptyStringKey()
@@ -51,9 +49,18 @@ public function testOpenSslEncrpytionEncryptOnEmptyStringKey()
5149

5250
public function testSodiumEncrpytionEncryptOnEmptyStringKey()
5351
{
54-
if (extension_loaded('sodium')) {
55-
$this->expectException(\InvalidArgumentException::class);
56-
new Encryption(new SodiumEncryption(''));
57-
}
52+
$this->expectException(\InvalidArgumentException::class);
53+
new Encryption(new SodiumEncryption(''));
54+
}
55+
56+
public function testDecryptWithNoHash()
57+
{
58+
$encryption1 = new Encryption(new OpenSslEncryption('12345678990-=====-==='));
59+
$decryptedString1 = $encryption1->decrypt("wrong-hash");
60+
$this->assertFalse($decryptedString1);
61+
62+
$encryption2 = new Encryption(new SodiumEncryption('12345678990-=====-==='));
63+
$decryptedString2 = $encryption2->decrypt("wrong-hash");
64+
$this->assertFalse($decryptedString2);
5865
}
5966
}

tests/SecurePasswordTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ public function testCreateHashWithConfig()
1818
{
1919
$config = [
2020
'algo' => AlgorithmEnum::ARGON2I,
21-
'cost' => 10,
21+
'cost' => 12,
2222
'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
2323
'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
2424
'threads' => PASSWORD_ARGON2_DEFAULT_THREADS
2525
];
26-
26+
2727
$password = new SecurePassword($config);
2828
$hash = $password->createHash('my_password')->getHash();
2929

@@ -67,22 +67,22 @@ public function testCreateWithAlgorithm()
6767

6868
public function testCreateWithOtherAlgorithm()
6969
{
70-
/* Example 1 */
70+
// Example 1
7171
$password = new SecurePassword();
7272
$hash = $password->useArgon2()->createHash('my_password')->getHash();
7373
$needs = $password->useDefault()->needsRehash('my_password', $hash);
7474

7575
$this->assertIsString($needs);
7676

77-
/* Example 2 */
77+
// Example 2
7878
$password_bcrypt = new SecurePassword([
7979
'algo' => AlgorithmEnum::BCRYPT
8080
]);
8181

8282
$needs2 = $password_bcrypt->needsRehash('my_password', $hash);
8383
$this->assertIsString($needs2);
8484

85-
/* Example 3 */
85+
// Example 3
8686
$password_argon = new SecurePassword([
8787
'algo' => AlgorithmEnum::ARGON2I
8888
]);
@@ -101,7 +101,7 @@ public function testCreateAndVerifyHashChained()
101101
{
102102
$password = new SecurePassword();
103103
$hash = $password->createHash('my_password')->verifyHash();
104-
$hash2 = $password->verifyHash(md5('WrongHashTest'), $this->wait_time);
104+
$hash2 = $password->verifyHash(md5('WrongHashTest'), wait_microseconds: $this->wait_time);
105105

106106
$this->assertTrue($hash);
107107
$this->assertFalse($hash2);

0 commit comments

Comments
 (0)