-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #583 from jolicode/crypto-function
Add support for some crypto functions
- Loading branch information
Showing
14 changed files
with
345 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# Cryptography helpers | ||
|
||
## The `encrypt_with_password()` function | ||
|
||
Castor provides a `encrypt_with_password()` function to allow to encrypt a | ||
content with a password: | ||
|
||
```php | ||
use Castor\Attribute\AsArgument; | ||
use Castor\Attribute\AsTask; | ||
|
||
use function Castor\encrypt_with_password; | ||
use function Castor\io; | ||
|
||
#[AsTask(description: 'Encrypt content with a password')] | ||
function encrypt(#[AsArgument()] string $content = "Hello you!"): void | ||
{ | ||
io()->writeln(encrypt_with_password($content, 'my super secret password')); | ||
} | ||
``` | ||
|
||
> [!NOTE] | ||
> Under the hood, Castor use libsodium for encryption. | ||
## The `decrypt_with_password()` function | ||
|
||
Castor provides a `decrypt_with_password()` function to allow to decrypt a | ||
content with a password: | ||
|
||
```php | ||
use Castor\Attribute\AsArgument; | ||
use Castor\Attribute\AsTask; | ||
|
||
use function Castor\decrypt_with_password; | ||
use function Castor\io; | ||
|
||
#[AsTask(description: 'Decrypt content with a password',)] | ||
function decrypt(string $content): void | ||
{ | ||
io()->writeln(decrypt_with_password($content, 'my super secret password')); | ||
} | ||
``` | ||
|
||
## The `encrypt_file_with_password()` function | ||
|
||
Castor provides a `encrypt_file_with_password()` function to allow to encrypt a | ||
file with a password: | ||
|
||
```php | ||
use Castor\Attribute\AsArgument; | ||
use Castor\Attribute\AsTask; | ||
|
||
use function Castor\encrypt_file_with_password; | ||
use function Castor\io; | ||
|
||
#[AsTask(description: 'Encrypt file with a password')] | ||
function encrypt_file(string $file): void | ||
{ | ||
encrypt_file_with_password($file, 'my super secret password'); | ||
} | ||
``` | ||
|
||
## The `decrypt_file_with_password()` function | ||
|
||
Castor provides a `decrypt_file_with_password()` function to allow to decrypt a | ||
file with a password: | ||
|
||
```php | ||
use Castor\Attribute\AsArgument; | ||
use Castor\Attribute\AsTask; | ||
|
||
use function Castor\decrypt_file_with_password; | ||
use function Castor\io; | ||
|
||
#[AsTask(description: 'Decrypt file with a password')] | ||
function decrypt_file(string $file): void | ||
{ | ||
decrypt_file_with_password($file, 'my super secret password'); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
namespace crypto; | ||
|
||
use Castor\Attribute\AsArgument; | ||
use Castor\Attribute\AsTask; | ||
|
||
use function Castor\decrypt_file_with_password; | ||
use function Castor\decrypt_with_password; | ||
use function Castor\encrypt_file_with_password; | ||
use function Castor\encrypt_with_password; | ||
use function Castor\io; | ||
|
||
#[AsTask(description: 'Encrypt content with a password')] | ||
function encrypt(#[AsArgument()] string $content = 'Hello you!'): void | ||
{ | ||
io()->writeln(encrypt_with_password($content, 'my super secret password')); | ||
} | ||
|
||
#[AsTask(description: 'Decrypt content with a password', )] | ||
function decrypt(string $content): void | ||
{ | ||
io()->writeln(decrypt_with_password($content, 'my super secret password')); | ||
} | ||
|
||
#[AsTask(description: 'Encrypt file with a password')] | ||
function encrypt_file(string $file): void | ||
{ | ||
encrypt_file_with_password($file, 'my super secret password'); | ||
} | ||
|
||
#[AsTask(description: 'Decrypt file with a password')] | ||
function decrypt_file(string $file): void | ||
{ | ||
decrypt_file_with_password($file, 'my super secret password'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<?php | ||
|
||
namespace Castor\Helper; | ||
|
||
use Psr\Log\LoggerInterface; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
class SymmetricCrypto | ||
{ | ||
public function __construct( | ||
private LoggerInterface $logger, | ||
) { | ||
} | ||
|
||
public function encrypt(#[\SensitiveParameter] string $content, #[\SensitiveParameter] string $password): string | ||
{ | ||
if (!\extension_loaded('sodium')) { | ||
throw new \RuntimeException('The sodium extension is required to use crypto functions.'); | ||
} | ||
|
||
if (mb_strlen($password) < 8) { | ||
$this->logger->warning('The password is too short. It is recommended to use at least 8 characters.'); | ||
} | ||
|
||
$nonce = random_bytes(\SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); | ||
|
||
$salt = random_bytes(\SODIUM_CRYPTO_PWHASH_SALTBYTES); | ||
|
||
$key = sodium_crypto_pwhash( | ||
\SODIUM_CRYPTO_SECRETBOX_KEYBYTES, | ||
$password, | ||
$salt, | ||
\SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, | ||
\SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE | ||
); | ||
|
||
sodium_memzero($password); | ||
|
||
$encrypted = sodium_crypto_secretbox($content, $nonce, $key); | ||
|
||
sodium_memzero($content); | ||
sodium_memzero($key); | ||
|
||
return base64_encode($nonce . $salt . $encrypted); | ||
} | ||
|
||
public function decrypt(string $encoded, #[\SensitiveParameter] string $password): string | ||
{ | ||
if (!\extension_loaded('sodium')) { | ||
throw new \RuntimeException('The sodium extension is required to use crypto functions.'); | ||
} | ||
|
||
$decoded = base64_decode($encoded); | ||
|
||
$nonce = substr($decoded, 0, \SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); | ||
if (\SODIUM_CRYPTO_SECRETBOX_NONCEBYTES !== \strlen($nonce)) { | ||
throw new \RuntimeException('Failed to decrypt the content. Impossible to extract nonce.'); | ||
} | ||
|
||
$salt = substr($decoded, \SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, \SODIUM_CRYPTO_PWHASH_SALTBYTES); | ||
if (\SODIUM_CRYPTO_PWHASH_SALTBYTES !== \strlen($salt)) { | ||
throw new \RuntimeException('Failed to decrypt the content. Impossible to extract salt.'); | ||
} | ||
|
||
$cipherText = substr($decoded, \SODIUM_CRYPTO_PWHASH_SALTBYTES + \SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); | ||
|
||
$key = sodium_crypto_pwhash( | ||
\SODIUM_CRYPTO_SECRETBOX_KEYBYTES, | ||
$password, | ||
$salt, | ||
\SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, | ||
\SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE | ||
); | ||
|
||
sodium_memzero($password); | ||
|
||
$decrypted = sodium_crypto_secretbox_open($cipherText, $nonce, $key); | ||
if (false === $decrypted) { | ||
throw new \RuntimeException('Failed to decrypt the content.'); | ||
} | ||
|
||
sodium_memzero($key); | ||
|
||
return $decrypted; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
namespace Castor\Tests\Examples; | ||
|
||
use Castor\Tests\Helper\OutputCleaner; | ||
use Castor\Tests\TaskTestCase; | ||
use Symfony\Component\Process\Exception\ProcessFailedException; | ||
|
||
class CryptoDecryptTest extends TaskTestCase | ||
{ | ||
// crypto:decrypt | ||
public function test(): void | ||
{ | ||
$process = $this->runTask(['crypto:decrypt', 'rEg3vPkg1De1I91jmK4cuYlP5Pov1Fm0CVqkG3kFFtwjbSM6zi5yB5UugNppdFkOtiyzcbKr1QbCkF+qa2ymgL8PRw==']); | ||
|
||
if (0 !== $process->getExitCode()) { | ||
throw new ProcessFailedException($process); | ||
} | ||
|
||
$output = OutputCleaner::cleanOutput($process->getOutput()); | ||
|
||
$this->assertSame("hello there\n", $output); | ||
$this->assertSame('', $process->getErrorOutput()); | ||
} | ||
} |
Oops, something went wrong.