From d1f7fd98bf6c4f3cd3395d1118875403fb711bf6 Mon Sep 17 00:00:00 2001 From: Luca Castelnuovo Date: Sun, 26 May 2024 15:33:46 +0200 Subject: [PATCH 1/3] v2 --- README.md | 22 ++++-------------- config/age.php | 5 ---- src/{LaravelAge.php => Age.php} | 10 ++++---- src/Facades/{LaravelAge.php => Age.php} | 6 ++--- src/LaravelAgeServiceProvider.php | 3 +-- src/PrivateKey.php | 31 +++++++++++++------------ tests/LaravelAgeTest.php | 30 ++++++++++++------------ 7 files changed, 44 insertions(+), 63 deletions(-) delete mode 100644 config/age.php rename src/{LaravelAge.php => Age.php} (87%) rename src/Facades/{LaravelAge.php => Age.php} (55%) diff --git a/README.md b/README.md index 249494c..6953293 100644 --- a/README.md +++ b/README.md @@ -25,35 +25,21 @@ You can install the package via composer: composer require castelnuovo/laravel-age ``` -You can publish the config file with: - -```bash -php artisan vendor:publish --tag="laravel-age-config" -``` - -This is the contents of the published config file: - -```php -return [ - 'identity' => env('AGE_IDENTITY') -]; -``` - ## Usage ```php -use Castelnuovo\LaravelAge\LaravelAge; +use Castelnuovo\LaravelAge\Age; $message = 'Hello World!'; -$age = LaravelAge::generateKeypair(); +$age = Age::generateKeypair(); $privateKey = $age->getPrivateKey(); $publicKey = $age->getPublicKey(); -$age2 = new LaravelAge(publicKey: $publicKey); +$age2 = new Age(publicKey: $publicKey); $encrypted_message = $age2->encrypt($message); -$age3 = new LaravelAge(privateKey: $privateKey); +$age3 = new Age(privateKey: $privateKey); $decrypted_message = $age3->decrypt($encrypted_message); echo $message === $decrypted_message ? 'Success' : 'Failed'; diff --git a/config/age.php b/config/age.php deleted file mode 100644 index ff59240..0000000 --- a/config/age.php +++ /dev/null @@ -1,5 +0,0 @@ - env('AGE_IDENTITY'), -]; diff --git a/src/LaravelAge.php b/src/Age.php similarity index 87% rename from src/LaravelAge.php rename to src/Age.php index a68f4f7..a217c09 100755 --- a/src/LaravelAge.php +++ b/src/Age.php @@ -4,14 +4,14 @@ use Exception; -class LaravelAge +class Age { public function __construct(private ?PrivateKey $privateKey = null, private ?PublicKey $publicKey = null) { /* If no keys are provided, use the one from .env or create a pair. */ if (! isset($privateKey) && ! isset($publicKey)) { /** @phpstan-ignore-next-line */ - $this->privateKey = new PrivateKey(config('laravel-age.identity')); + $this->privateKey = PrivateKey::generate(); $this->publicKey = $this->privateKey->getPublicKey(); } @@ -21,11 +21,11 @@ public function __construct(private ?PrivateKey $privateKey = null, private ?Pub } } - public static function generateKeypair(): LaravelAge + public static function generateKeypair(): Age { - $privateKey = new PrivateKey(); + $privateKey = PrivateKey::generate(); - return new LaravelAge($privateKey, $privateKey->getPublicKey()); + return new Age($privateKey, $privateKey->getPublicKey()); } public function getPublicKey(): PublicKey diff --git a/src/Facades/LaravelAge.php b/src/Facades/Age.php similarity index 55% rename from src/Facades/LaravelAge.php rename to src/Facades/Age.php index b9a2953..4f0dca6 100644 --- a/src/Facades/LaravelAge.php +++ b/src/Facades/Age.php @@ -5,12 +5,12 @@ use Illuminate\Support\Facades\Facade; /** - * @see \Castelnuovo\LaravelAge\LaravelAge + * @see \Castelnuovo\LaravelAge\Age */ -class LaravelAge extends Facade +class Age extends Facade { protected static function getFacadeAccessor() { - return \Castelnuovo\LaravelAge\LaravelAge::class; + return \Castelnuovo\LaravelAge\Age::class; } } diff --git a/src/LaravelAgeServiceProvider.php b/src/LaravelAgeServiceProvider.php index bd8535d..da93fdb 100644 --- a/src/LaravelAgeServiceProvider.php +++ b/src/LaravelAgeServiceProvider.php @@ -15,7 +15,6 @@ public function configurePackage(Package $package): void * More info: https://github.com/spatie/laravel-package-tools */ $package - ->name('laravel-age') - ->hasConfigFile(); + ->name('laravel-age'); } } diff --git a/src/PrivateKey.php b/src/PrivateKey.php index 7c86d7f..b5d12b9 100644 --- a/src/PrivateKey.php +++ b/src/PrivateKey.php @@ -13,21 +13,8 @@ class PrivateKey { private string $privateKey; - public function __construct(string $privateKey = '') + public function __construct(string $privateKey) { - if (! $privateKey) { - $result = Process::pipe([ - 'age-keygen', - 'grep -E "^AGE-SECRET-KEY-[A-Za-z0-9]{59}$"', - ]); - - if ($result->failed()) { - throw new Exception('Failed to generate private key!'); - } - - $privateKey = $result->output(); - } - $privateKey = str($privateKey)->trim(); if (! $privateKey->startsWith('AGE-SECRET-KEY-') || $privateKey->length() !== 74) { @@ -37,6 +24,20 @@ public function __construct(string $privateKey = '') $this->privateKey = $privateKey; } + public static function generate(): self + { + $result = Process::pipe([ + 'age-keygen', + 'grep -E "^AGE-SECRET-KEY-[A-Za-z0-9]{59}$"', + ]); + + if ($result->failed()) { + throw new Exception('Failed to generate private key!'); + } + + return new self($result->output()); + } + public function encode(): string { return $this->privateKey; @@ -62,7 +63,7 @@ public function decrypt(string $message, bool $base64): string $ulid = Str::ulid(); $dir = TemporaryDirectory::make()->deleteWhenDestroyed(); - $data = $base64 ? base64_decode(str_replace(['-', '_'], ['+', '/'], $message)) : $message; + $data = $base64 ? base64_decode($message, strict: true) : $message; Storage::build(['driver' => 'local', 'root' => $dir->path()])->put($ulid, $data); /** diff --git a/tests/LaravelAgeTest.php b/tests/LaravelAgeTest.php index eb33a26..e271cc3 100644 --- a/tests/LaravelAgeTest.php +++ b/tests/LaravelAgeTest.php @@ -1,15 +1,15 @@ encode())->toBeString(); + expect(PrivateKey::generate()->encode())->toBeString(); }); it('can generate public key', function () { - $publicKey = (new PrivateKey())->getPublicKey(); + $publicKey = PrivateKey::generate()->getPublicKey(); expect($publicKey->encode())->toBeString(); }); @@ -37,9 +37,9 @@ })->throws(Exception::class); it('can generate keypair', function () { - $age = LaravelAge::generateKeypair(); + $age = Age::generateKeypair(); - expect($age)->toBeInstanceOf(LaravelAge::class); + expect($age)->toBeInstanceOf(Age::class); expect($age->getPrivateKey())->toBeInstanceOf(PrivateKey::class); expect($age->getPublicKey())->toBeInstanceOf(PublicKey::class); }); @@ -47,7 +47,7 @@ it('can encrypt with an provided private key', function () { $encodeKey = 'AGE-SECRET-KEY-1TKGRTQP4H79MTNHVDRSA3L0CS7MFSMW0DQX80CWP0JDUFL97RRJSPK9777'; $privateKey = new PrivateKey($encodeKey); - $age = new LaravelAge(privateKey: $privateKey); + $age = new Age(privateKey: $privateKey); expect($age->encrypt('message'))->toBeString(); }); @@ -55,7 +55,7 @@ it('can encrypt with an provided public key', function () { $encodeKey = 'age1xqrfpqxz55ersvu6mmhwzcctqk27ppnatms7p9zruclrm8tt4y0q3apxuc'; $publicKey = new PublicKey($encodeKey); - $age = new LaravelAge(publicKey: $publicKey); + $age = new Age(publicKey: $publicKey); expect($age->encrypt('message'))->toBeString(); }); @@ -63,7 +63,7 @@ it('can decrypt with an provided private key (using base64)', function () { $encodeKey = 'AGE-SECRET-KEY-1TKGRTQP4H79MTNHVDRSA3L0CS7MFSMW0DQX80CWP0JDUFL97RRJSPK9777'; $privateKey = new PrivateKey($encodeKey); - $age = new LaravelAge(privateKey: $privateKey); + $age = new Age(privateKey: $privateKey); $message = 'Hello, World!'; $encrypted = $age->encrypt($message); @@ -75,7 +75,7 @@ it('can decrypt with an provided private key (without using base64)', function () { $encodeKey = 'AGE-SECRET-KEY-1TKGRTQP4H79MTNHVDRSA3L0CS7MFSMW0DQX80CWP0JDUFL97RRJSPK9777'; $privateKey = new PrivateKey($encodeKey); - $age = new LaravelAge(privateKey: $privateKey); + $age = new Age(privateKey: $privateKey); $message = 'Hello, World!'; $encrypted = $age->encrypt($message, false); @@ -87,7 +87,7 @@ it('cannot decrypt with an provided public key', function () { $encodeKey = 'age1xqrfpqxz55ersvu6mmhwzcctqk27ppnatms7p9zruclrm8tt4y0q3apxuc'; $publicKey = new PublicKey($encodeKey); - $age = new LaravelAge(publicKey: $publicKey); + $age = new Age(publicKey: $publicKey); $message = 'Hello, World!'; $encrypted = $age->encrypt($message); @@ -97,7 +97,7 @@ it('cannot decrypt with an provided private key (using base64 only to encrypt)', function () { $encodeKey = 'age1xqrfpqxz55ersvu6mmhwzcctqk27ppnatms7p9zruclrm8tt4y0q3apxuc'; $publicKey = new PublicKey($encodeKey); - $age = new LaravelAge(publicKey: $publicKey); + $age = new Age(publicKey: $publicKey); $message = 'Hello, World!'; $encrypted = $age->encrypt($message); @@ -107,7 +107,7 @@ it('cannot decrypt with an provided private key (using base64 only to decrypt)', function () { $encodeKey = 'age1xqrfpqxz55ersvu6mmhwzcctqk27ppnatms7p9zruclrm8tt4y0q3apxuc'; $publicKey = new PublicKey($encodeKey); - $age = new LaravelAge(publicKey: $publicKey); + $age = new Age(publicKey: $publicKey); $message = 'Hello, World!'; $encrypted = $age->encrypt($message, false); @@ -117,14 +117,14 @@ it('runs usage code from readme', function () { $message = 'Hello World!'; - $age = LaravelAge::generateKeypair(); + $age = Age::generateKeypair(); $privateKey = $age->getPrivateKey(); $publicKey = $age->getPublicKey(); - $age2 = new LaravelAge(publicKey: $publicKey); + $age2 = new Age(publicKey: $publicKey); $encrypted_message = $age2->encrypt($message); - $age3 = new LaravelAge(privateKey: $privateKey); + $age3 = new Age(privateKey: $privateKey); $decrypted_message = $age3->decrypt($encrypted_message); expect($message)->toBe($decrypted_message); From 94851a98df1c49d49eb705f71b3eafec83c2ac43 Mon Sep 17 00:00:00 2001 From: Luca Castelnuovo Date: Sun, 26 May 2024 15:36:39 +0200 Subject: [PATCH 2/3] Remove facade --- composer.json | 5 +---- src/Facades/Age.php | 16 ---------------- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 src/Facades/Age.php diff --git a/composer.json b/composer.json index 96f779f..e618675 100644 --- a/composer.json +++ b/composer.json @@ -74,10 +74,7 @@ "laravel": { "providers": [ "Castelnuovo\\LaravelAge\\LaravelAgeServiceProvider" - ], - "aliases": { - "LaravelAge": "Castelnuovo\\LaravelAge\\Facades\\LaravelAge" - } + ] } }, "minimum-stability": "dev", diff --git a/src/Facades/Age.php b/src/Facades/Age.php deleted file mode 100644 index 4f0dca6..0000000 --- a/src/Facades/Age.php +++ /dev/null @@ -1,16 +0,0 @@ - Date: Sun, 26 May 2024 15:43:38 +0200 Subject: [PATCH 3/3] phpstan issues --- phpstan.neon.dist | 1 - src/Age.php | 1 - src/PrivateKey.php | 4 ++++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f4a34f0..1ed9871 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -5,7 +5,6 @@ parameters: level: 9 paths: - src - - config tmpDir: build/phpstan checkOctaneCompatibility: true checkModelProperties: true diff --git a/src/Age.php b/src/Age.php index a217c09..5e18595 100755 --- a/src/Age.php +++ b/src/Age.php @@ -10,7 +10,6 @@ public function __construct(private ?PrivateKey $privateKey = null, private ?Pub { /* If no keys are provided, use the one from .env or create a pair. */ if (! isset($privateKey) && ! isset($publicKey)) { - /** @phpstan-ignore-next-line */ $this->privateKey = PrivateKey::generate(); $this->publicKey = $this->privateKey->getPublicKey(); } diff --git a/src/PrivateKey.php b/src/PrivateKey.php index b5d12b9..d00ba6b 100644 --- a/src/PrivateKey.php +++ b/src/PrivateKey.php @@ -64,6 +64,10 @@ public function decrypt(string $message, bool $base64): string $dir = TemporaryDirectory::make()->deleteWhenDestroyed(); $data = $base64 ? base64_decode($message, strict: true) : $message; + if (! $data) { + throw new Exception('Invalid message provided!'); + } + Storage::build(['driver' => 'local', 'root' => $dir->path()])->put($ulid, $data); /**