Skip to content

Commit 5d98210

Browse files
committed
added crypt sodium
1 parent 295af88 commit 5d98210

13 files changed

+495
-102
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
vendor/
22
composer.lock
33
index.php
4-
.phpunit.result.cache
4+
.phpunit.result.cache
5+
.phpunit.cache

CHANGELOG.md

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

3+
## v3.0.0 - (2022-11-11)
4+
5+
### Added
6+
7+
- Added PHP 8.2 minimum version
8+
- Added classes for encryption: OpenSSL and Sodium support
9+
- Added `PepperTrait` trait to handle the peeper separately from the `SecuryPassword` class
10+
11+
### Changed
12+
13+
- Changed class structure
14+
15+
-----------------------------------------------------------
16+
317
## v2.0.0 - (2022-09-21)
418

519
### Added

README.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Unlike just using `password_hash`, SecurePassword adds a secret entry (commonly
88

99
## Requirements
1010

11-
PHP >= 8.0
11+
PHP >= 8.2
1212

1313
## Installing via Composer
1414

@@ -150,6 +150,45 @@ $hash = $password->useArgon2(false, PASSWORD_ARGON2_DEFAULT_MEMORY_COST, PASSWOR
150150
$hash = $password->useArgon2(true, PASSWORD_ARGON2_DEFAULT_MEMORY_COST, PASSWORD_ARGON2_DEFAULT_TIME_COST, PASSWORD_ARGON2_DEFAULT_THREADS)->createHash('my_password');
151151
```
152152

153+
## Using OpenSSL and Sodium encryption
154+
155+
You can use OpenSSL and Sodium encryption using the `Encryption` class:
156+
157+
```php
158+
use SecurePassword\Encrypt\Encryption;
159+
160+
$encryption = new Encryption('your-key');
161+
162+
//Encrypt the message
163+
$encrypt = $encryption->encrypt("This is a text");
164+
165+
echo $encrypt;
166+
```
167+
168+
You can decrypt token by calling decrypt method:
169+
170+
```php
171+
$encryption = new Encryption('your-key');
172+
173+
//Decrypt the message
174+
$decrypt = $encryption->decrypt($encrypt);
175+
176+
echo $decrypt;
177+
```
178+
179+
You can pass supported adapter to class like:
180+
181+
Use of OpenSSL
182+
```php
183+
$encryption = new Encryption(new OpenSslEncryption('your-key'));
184+
```
185+
Use of Sodium
186+
```php
187+
$encryption = new Encryption(new SodiumEncryption('your-key'));
188+
```
189+
190+
Default openSSL will use, you can use any one you want.
191+
153192
## Changing the secret entry (recommended)
154193

155194
It is recommended to change the secret entry (or pepper) that will be added to your password. Use `setPepper` to change.
@@ -159,6 +198,16 @@ $password = new SecurePassword();
159198
$password->setPepper('new_pepper');
160199
```
161200

201+
By default, the `setPepper` method uses OpenSSL encryption. However, you can use Sodium encryption if you want.
202+
203+
```php
204+
// Use OpenSSL
205+
$password->setPepper('new_pepper', 'openssl');
206+
207+
// Use Sodium
208+
$password->setPepper('new_pepper', 'sodium');
209+
```
210+
162211
## Getting the ideal encryption cost
163212

164213
Here's a quick little function that will help you determine what cost parameter you should be using for your server to make sure you are within this range.

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
"php-password"
99
],
1010
"require": {
11-
"php": ">=8.0"
11+
"php": "^8.2"
1212
},
1313
"require-dev": {
14-
"phpunit/phpunit": "^9.5"
14+
"phpunit/phpunit": "^10"
1515
},
1616
"autoload": {
1717
"psr-4": {

phpunit.xml

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
3-
<phpunit bootstrap="vendor/autoload.php"
4-
colors="true"
5-
verbose="true"
6-
stopOnFailure="false">
7-
<testsuites>
8-
<testsuite name="Password Test Suite">
9-
<directory>tests</directory>
10-
</testsuite>
11-
</testsuites>
12-
</phpunit>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php" colors="true" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.4/phpunit.xsd" cacheDirectory=".phpunit.cache">
3+
<testsuites>
4+
<testsuite name="Password Test Suite">
5+
<directory>tests</directory>
6+
</testsuite>
7+
</testsuites>
8+
</phpunit>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace SecurePassword\Encrypt\Adapter;
4+
5+
interface AbstractAdapterInterface
6+
{
7+
/**
8+
* Encrypt the message.
9+
*
10+
* @param mixed $data data to be encrypted
11+
12+
* @return mixed
13+
*/
14+
public function encrypt(mixed $data): mixed;
15+
16+
/**
17+
* Decrypt the message.
18+
*
19+
* @param mixed $token encrypted token
20+
21+
* @return mixed
22+
*/
23+
public function decrypt(mixed $token): mixed;
24+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace SecurePassword\Encrypt\Adapter;
4+
5+
class OpenSslEncryption implements AbstractAdapterInterface
6+
{
7+
/**
8+
* Store the cipher iv.
9+
*
10+
* @var string
11+
*/
12+
private string $iv;
13+
14+
/**
15+
* Store secret key.
16+
*
17+
* @var string
18+
*/
19+
private string $key;
20+
21+
/**
22+
* Cipher.
23+
*
24+
* @var string
25+
*/
26+
private string $cipher = 'AES-256-CBC';
27+
28+
/**
29+
* __Construct.
30+
*
31+
* @since 1.0.0
32+
*
33+
* @return void
34+
*/
35+
public function __construct(string $key)
36+
{
37+
if ($key === '') {
38+
throw new \InvalidArgumentException('The key should not be empty string.');
39+
}
40+
41+
$this->iv = openssl_random_pseudo_bytes($this->ivBytes($this->cipher));
42+
$this->key = hash('sha512', $key);
43+
}
44+
45+
/**
46+
* Encrypt the message.
47+
*
48+
* @param mixed $data => data to be encrypted
49+
*
50+
* @return token
51+
*/
52+
public function encrypt(mixed $data): mixed
53+
{
54+
return base64_encode(openssl_encrypt($data, $this->cipher, $this->key, 0, $this->iv).'&&'.bin2hex($this->iv));
55+
}
56+
57+
/**
58+
* Decrypt the message.
59+
*
60+
* @param mixed $token => encrypted token
61+
62+
* @return mix-data
63+
*/
64+
public function decrypt(mixed $token): mixed
65+
{
66+
$token = base64_decode($token);
67+
list($token, $this->iv) = explode('&&', $token);
68+
69+
return openssl_decrypt($token, $this->cipher, $this->key, 0, hex2bin($this->iv));
70+
}
71+
72+
/**
73+
* Get the length of cipher.
74+
*
75+
* @param $method
76+
77+
* @return int
78+
*/
79+
protected function ivBytes(string $method): int
80+
{
81+
return openssl_cipher_iv_length($method);
82+
}
83+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace SecurePassword\Encrypt\Adapter;
4+
5+
class SodiumEncryption implements AbstractAdapterInterface
6+
{
7+
/**
8+
* Store secret key.
9+
*
10+
* @var string
11+
*/
12+
private string $key;
13+
14+
/**
15+
* __Construct.
16+
*
17+
* @param string $key
18+
*/
19+
public function __construct(string $key)
20+
{
21+
if ($key === '') {
22+
throw new \InvalidArgumentException('The key should not be empty string.');
23+
}
24+
25+
if (!function_exists('sodium_crypto_secretbox_keygen')) {
26+
throw new \Exception('The sodium php extension does not installed or enabled', 500);
27+
}
28+
29+
//should use user define key.
30+
$this->key = substr(hash('sha512', $key), 0, 32);
31+
}
32+
33+
/**
34+
* Encrypt the message.
35+
*
36+
* @param string $data data to be encrypted
37+
*
38+
* @return mixed
39+
*/
40+
public function encrypt(mixed $data): mixed
41+
{
42+
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
43+
$token = base64_encode($nonce.sodium_crypto_secretbox($data, $nonce, $this->key).'&&'.$this->key);
44+
45+
return $token;
46+
}
47+
48+
/**
49+
* Decrypt the message.
50+
*
51+
* @param mixed $token encrypted token
52+
53+
* @return mixed
54+
*/
55+
public function decrypt(mixed $token): mixed
56+
{
57+
$decoded = base64_decode($token);
58+
list($decoded, $this->key) = explode('&&', $decoded);
59+
60+
if ($decoded === false) {
61+
throw new \Exception('The decoding failed');
62+
}
63+
64+
if (mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
65+
throw new \Exception('The token was truncated');
66+
}
67+
68+
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
69+
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
70+
71+
$plain = sodium_crypto_secretbox_open($ciphertext,
72+
$nonce, $this->key);
73+
74+
if ($plain === false) {
75+
throw new \Exception('The message was tampered with in transit');
76+
}
77+
78+
return $plain;
79+
}
80+
}

src/Encrypt/Encryption.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace SecurePassword\Encrypt;
4+
5+
use SecurePassword\Encrypt\Adapter\AbstractAdapterInterface;
6+
7+
class Encryption
8+
{
9+
/**
10+
* @param AbstractAdapterInterface $adapter
11+
*/
12+
public function __construct(
13+
private AbstractAdapterInterface $adapter
14+
)
15+
{
16+
if ($this->adapter === null) {
17+
throw new \InvalidArgumentException('The adapter class should not be null.');
18+
}
19+
}
20+
21+
/**
22+
* Encrypt the message.
23+
*
24+
* @param mixed $data data to be encrypted
25+
*
26+
* @return mixed
27+
*/
28+
public function encrypt(mixed $data): mixed
29+
{
30+
return $this->adapter->encrypt($data);
31+
}
32+
33+
/**
34+
* Decrypt the message.
35+
*
36+
* @param mixed $token encrypted token
37+
38+
* @return mixed
39+
*/
40+
public function decrypt(mixed $token): mixed
41+
{
42+
return $this->adapter->decrypt($token);
43+
}
44+
}

0 commit comments

Comments
 (0)