Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"analyze": "Lance l'analyse statique du code du framework",
"cs": "Vérifie les normes de codage",
"cs:fix": "Corrige le style de codage",
"phpstan:baseline": "Exécute PHPStan puis transférer toutes les erreurs vers la ligne de base.",
"phpstan:baseline": "Exécute PHPStan puis transférer toutes les erreurs vers le fichier de baseline.",
"phpstan:check": "Exécute PHPStan avec la prise en charge des identifiants",
"test": "Execute les tests unitaires"
},
Expand Down
18 changes: 18 additions & 0 deletions phpstan-baseline.php
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,24 @@
'count' => 3,
'path' => __DIR__ . '/src/Router/Dispatcher.php',
];
$ignoreErrors[] = [
// identifier: booleanAnd.alwaysFalse
'message' => '#^Result of && is always false\\.$#',
'count' => 1,
'path' => __DIR__ . '/src/Security/Hashing/Handlers/ArgonHandler.php',
];
$ignoreErrors[] = [
// identifier: identical.alwaysFalse
'message' => '#^Strict comparison using \\=\\=\\= between \'standard\' and \'sodium\' will always evaluate to false\\.$#',
'count' => 1,
'path' => __DIR__ . '/src/Security/Hashing/Handlers/ArgonHandler.php',
];
$ignoreErrors[] = [
// identifier: method.notFound
'message' => '#^Call to an undefined method BlitzPHP\\\\Contracts\\\\Security\\\\HasherInterface\\:\\:verifyConfiguration\\(\\)\\.$#',
'count' => 1,
'path' => __DIR__ . '/src/Security/Hashing/Hasher.php',
];
$ignoreErrors[] = [
// identifier: class.notFound
'message' => '#^Call to method directive\\(\\) on an unknown class Jenssegers\\\\Blade\\\\Blade\\.$#',
Expand Down
26 changes: 13 additions & 13 deletions spec/system/framework/Security/Encryption/Encryption.spec.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@
});
});

describe('Service', static function (): void {
it(': Le service encrypter fonctionne', static function (): void {
describe('Service', function (): void {
it(': Le service encrypter fonctionne', function (): void {
$config = config('encryption');
$config['driver'] = 'OpenSSL';
$config['key'] = 'anything';
Expand All @@ -77,32 +77,32 @@
expect($encrypter)->toBeAnInstanceOf(EncrypterInterface::class);
});

it(': Le service encrypter leve une exception si le pilote est mauvais', static function (): void {
it(': Le service encrypter leve une exception si le pilote est mauvais', function (): void {
// ask for a bad driver
$config = config('encryption');
$config['driver'] = 'Bogus';
$config['key'] = 'anything';

expect(static function () use ($config): void {
expect(function () use ($config): void {
service('encrypter', $config);
})->toThrow(new EncryptionException());
});

it(": Le service encrypter leve une exception s'il n'y a pas de cle", static function (): void {
expect(static function (): void {
it(": Le service encrypter leve une exception s'il n'y a pas de cle", function (): void {
expect(function (): void {
service('encrypter');
})->toThrow(new EncryptionException());
});

it(': Service encrypter partagé', static function (): void {
it(': Service encrypter partagé', function (): void {
$config = config('encryption');
$config['driver'] = 'OpenSSL';
$config['key'] = 'anything';

$encrypter = single_service('encrypter', $config);
$encrypter = service('encrypter', $config);

$config['key'] = 'Abracadabra';
$encrypter = single_service('encrypter', $config);
$encrypter = service('encrypter', $config, true);

expect($encrypter->key)->toBe('anything');
});
Expand All @@ -120,8 +120,8 @@
});
});

describe('Decryptage', static function (): void {
it(': Decrypte une chaine codée avec AES-128-CBC', static function (): void {
describe('Decryptage', function (): void {
it(': Decrypte une chaine codée avec AES-128-CBC', function (): void {
$config = config('encryption');
$config['driver'] = 'OpenSSL';
$config['key'] = hex2bin('64c70b0b8d45b80b9eba60b8b3c8a34d0193223d20fea46f8644b848bf7ce67f');
Expand All @@ -138,7 +138,7 @@
expect($decrypted)->toBe($expected);
});

it(': Decrypte une chaine codée avec AES-256-CTR', static function (): void {
it(': Decrypte une chaine codée avec AES-256-CTR', function (): void {
$config = config('encryption');
$config['driver'] = 'OpenSSL';
$config['key'] = hex2bin('64c70b0b8d45b80b9eba60b8b3c8a34d0193223d20fea46f8644b848bf7ce67f');
Expand All @@ -155,7 +155,7 @@
expect($decrypted)->toBe($expected);
});

it(': Decrypte une chaine codée avec base64_encode', static function (): void {
it(': Decrypte une chaine codée avec base64_encode', function (): void {
$config = config('encryption');
$config['driver'] = 'OpenSSL';
$config['key'] = hex2bin('64c70b0b8d45b80b9eba60b8b3c8a34d0193223d20fea46f8644b848bf7ce67f');
Expand Down
166 changes: 166 additions & 0 deletions spec/system/framework/Security/Hashing/Hasher.spec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

/**
* This file is part of Blitz PHP framework.
*
* (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

use BlitzPHP\Contracts\Security\HasherInterface;
use BlitzPHP\Exceptions\HashingException;
use BlitzPHP\Security\Hashing\Handlers\Argon2IdHandler;
use BlitzPHP\Security\Hashing\Handlers\ArgonHandler;
use BlitzPHP\Security\Hashing\Handlers\BcryptHandler;
use BlitzPHP\Security\Hashing\Hasher;
use BlitzPHP\Spec\ReflectionHelper;

use function Kahlan\expect;

describe('Security / Hashing', function (): void {
beforeEach(function (): void {
$this->hasher = new Hasher();
});

describe('Hasher', function (): void {
it(": L'utilisation d'un mauvais pilote leve une exception", function (): void {
$config = (object) config('hashing');
$config->driver = 'Bogus';

expect(function () use ($config): void {
$this->hasher->initialize($config);
})->toThrow(new HashingException());
});

it(": L'abscence du pilote leve une exception", function (): void {
// ask for a bad driver
$config = (object) config('hashing');
$config->driver = '';

expect(function () use ($config): void {
$this->hasher->initialize($config);
})->toThrow(new HashingException());
});

it(':isHashed', function () {
$hash = $this->hasher->make('password');

expect($this->hasher->isHashed($hash))->toBeTruthy();
expect($this->hasher->isHashed('password'))->toBeFalsy();
});
});

describe('Service', function (): void {
it(': Le service hashing fonctionne', function (): void {
$config = config('hashing');
$config['driver'] = 'bcrypt';

$hasher = service('hashing', $config);

expect($hasher)->toBeAnInstanceOf(HasherInterface::class);
});

it(': Le service hashing leve une exception si le pilote est mauvais', function (): void {
$config = config('hashing');
$config['driver'] = 'Bogus';

expect(function () use ($config): void {
single_service('hashing', $config);
})->toThrow(new HashingException());
});

it(': Service hashing partagé', function (): void {
$config = config('hashing');
$config['driver'] = 'bcrypt';

$hasher = service('hashing', $config);

$config['driver'] = 'argon';
$hasher = service('hashing', $config, true);

expect(ReflectionHelper::getPrivateProperty($hasher, 'driver'))->toBe('bcrypt');
});
});

describe(':check' , function () {
it('Les valeurs vide renvoient false', function () {
$hasher = service('hashing');
expect($hasher->check('', ''))->toBeFalsy();
expect($hasher->check('test', ''))->toBeFalsy();

$hasher = new BcryptHandler();
expect($hasher->check('password', ''))->toBeFalsy();
$hasher = new ArgonHandler();
expect($hasher->check('password', ''))->toBeFalsy();
$hasher = new Argon2IdHandler();
expect($hasher->check('password', ''))->toBeFalsy();
});
});

describe('Drivers', function (): void {
it(': Bcrypt', function (): void {
$hasher = new BcryptHandler();
$value = $hasher->make('password');

expect($value)->not->toBe('password');
expect($hasher->check('password', $value))->toBeTruthy();
expect($hasher->needsRehash($value))->toBeFalsy();
expect($hasher->needsRehash($value, ['rounds' => 1]))->toBeTruthy();
expect($hasher->info($value)['algoName'])->toBe('bcrypt');
expect($hasher->info($value)['options']['cost'])->toBeGreaterThan(11); // >= 12
expect($this->hasher->isHashed($value))->toBeTruthy();
});

it(': Argon', function (): void {
$hasher = new ArgonHandler();
$value = $hasher->make('password');

expect($value)->not->toBe('password');
expect($hasher->check('password', $value))->toBeTruthy();
expect($hasher->needsRehash($value))->toBeFalsy();
expect($hasher->needsRehash($value, ['threads' => 1]))->toBeTruthy();
expect($hasher->info($value)['algoName'])->toBe('argon2i');
expect($this->hasher->isHashed($value))->toBeTruthy();
});

it(': Argon2id', function (): void {
$hasher = new Argon2IdHandler();
$value = $hasher->make('password');

expect($value)->not->toBe('password');
expect($hasher->check('password', $value))->toBeTruthy();
expect($hasher->needsRehash($value))->toBeFalsy();
expect($hasher->needsRehash($value, ['threads' => 1]))->toBeTruthy();
expect($hasher->info($value)['algoName'])->toBe('argon2id');
expect($this->hasher->isHashed($value))->toBeTruthy();
});
});

describe('Verification', function (): void {
it('Bcrypt', function (): void {
$argonHandler = new ArgonHandler(['verify' => true]);
$argonHashed = $argonHandler->make('password');

expect(fn() => (new BcryptHandler(['verify' => true]))->check('password', $argonHashed))
->toThrow(new RuntimeException());
});

it('Argon', function (): void {
$argonHandler = new BcryptHandler(['verify' => true]);
$argonHashed = $argonHandler->make('password');

expect(fn() => (new ArgonHandler(['verify' => true]))->check('password', $argonHashed))
->toThrow(new RuntimeException());
});

it('Argon2id', function (): void {
$argonHandler = new BcryptHandler(['verify' => true]);
$argonHashed = $argonHandler->make('password');

expect(fn() => (new Argon2IdHandler(['verify' => true]))->check('password', $argonHashed))
->toThrow(new RuntimeException());
});
});
});
17 changes: 9 additions & 8 deletions src/Cli/Commands/Encryption/GenerateKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use BlitzPHP\Cli\Console\Command;
use BlitzPHP\Loader\DotEnv;
use BlitzPHP\Security\Encryption\Encryption;

/**
* Genere une nouvelle cle d'encryption.
Expand All @@ -32,20 +33,20 @@ class GenerateKey extends Command
/**
* @var string Description
*/
protected $description = 'Génère une nouvelle clé d\'encryption et la met dans le fichier `.env`.';
protected $description = 'Génère une nouvelle clé de chiffrememt et la met dans le fichier `.env`.';

/**
* @var string
*/
protected $service = 'Service de d\'encryption';
protected $service = 'Service de chiffrememt';

/**
* @var array Options
*/
protected $options = [
'--force' => 'Force l\'écrasement de clé existante dans le fichier `.env`.',
'--length' => ['La longeur de la chaîne aléatoire qui doit être retournée en bytes. Par défaut "32".', 32],
'--prefix' => ['Prefix à ajouter à la clé encodée (doit être hex2bin ou base64). Par défaut "hex2bin".', 'hex2bin'],
'--length' => ['La longueur de la chaîne aléatoire qui doit être retournée en bytes.', 32],
'--prefix' => ['Prefix à ajouter à la clé encodée (doit être hex2bin ou base64).', 'hex2bin'],
'--show' => 'Indique qu\'on souhaite afficher la clé générée dans le terminal après l\'avoir mis dans le fichier `.env`.',
];

Expand All @@ -68,7 +69,7 @@ public function execute(array $params)
$length = 32;
}

$this->task('Génération d\'une nouvelle clé d\'encryption');
$this->task('Génération d\'une nouvelle clé de chiffrememt');

$encodedKey = $this->generateRandomKey($prefix, $length);

Expand All @@ -79,20 +80,20 @@ public function execute(array $params)
}

if (! $this->setNewEncryptionKey($encodedKey)) {
$this->writer->error('Erreur dans la configuration d\'une nouvelle cle d\'encryption dans le fichier `.env`.', true);
$this->writer->error('Erreur dans la configuration d\'une nouvelle clé de chiffrememt dans le fichier `.env`.', true);

return;
}

$this->success('Une nouvelle clé d\'encryption de l\'application a été définie avec succès.');
$this->success('Une nouvelle clé de chiffrement de l\'application a été définie avec succès.');
}

/**
* Genere une cle et l'encode.
*/
protected function generateRandomKey(string $prefix, int $length): string
{
$key = random_bytes($length); // @todo prevoir l'utilisation d'une classe Encryption
$key = Encryption::createKey($length);

if ($prefix === 'hex2bin') {
return 'hex2bin:' . bin2hex($key);
Expand Down
Loading
Loading