Skip to content

Commit

Permalink
Merge pull request #14 from minvws/factory
Browse files Browse the repository at this point in the history
Implemented factory
  • Loading branch information
jaytaph authored Sep 30, 2022
2 parents 81bc46a + 8066c94 commit 5c8a8b7
Show file tree
Hide file tree
Showing 19 changed files with 345 additions and 181 deletions.
55 changes: 15 additions & 40 deletions src/CryptoServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

namespace MinVWS\Crypto\Laravel;

use MinVWS\Crypto\Laravel\Service\Sealbox;
use MinVWS\Crypto\Laravel\Service\Cms;
use MinVWS\Crypto\Laravel\Service\Signature;
use Illuminate\Support\ServiceProvider;
use MinVWS\Crypto\Laravel\Service\TempFileService;

Expand All @@ -17,51 +14,29 @@ public function register(): void
$this->app->bind(TempFileInterface::class, TempFileService::class);

$this->app->singleton(CmsCryptoInterface::class, function () {
if (function_exists('openssl_cms_encrypt') && !config('crypto.force_process_spawn', false)) {
return new Cms\NativeService(
config('crypto.cms.encryption_certificate_paths', []),
config('crypto.cms.decryption_certificate_path'),
config('crypto.cms.decryption_certificate_key_path'),
app(TempFileInterface::class),
);
}

return new Cms\ProcessSpawnService(
config('crypto.cms.encryption_certificate_paths', []),
config('crypto.cms.decryption_certificate_path'),
config('crypto.cms.decryption_certificate_key_path'),
Factory::createCmsCryptoService(
encryptionCertPaths: config('crypto.cms.encryption_certificate_paths'),
decryptionCertPath: config('crypto.cms.decryption_certificate_path'),
decryptionKeyPath: config('crypto.cms.decryption_certificate_key_path'),
forceProcessSpawn: config('crypto.force_process_spawn', false),
);
});

$this->app->singleton(SealboxCryptoInterface::class, function () {
return new Sealbox\SealboxService(
config('crypto.sealbox.private_key'),
config('crypto.sealbox.recipient_public_key')
Factory::createSealboxCryptoService(
privKey: config('crypto.sealbox.private_key'),
recipientPubKey: config('crypto.sealbox.recipient_pub_key'),
);
});

$this->app->singleton(SignatureCryptoInterface::class, function () {
$args = [
config('crypto.signature.x509_cert'),
config('crypto.signature.x509_key'),
config('crypto.signature.x509_pass', ''),
config('crypto.signature.x509_chain', ''),
app(TempFileInterface::class),
];

if (function_exists('openssl_cms_sign') && !config('crypto.force_process_spawn', false)) {
return new Signature\NativeService(...$args);
}

return new Signature\ProcessSpawnService(...$args);
});

$this->app->singleton(SignatureVerifyCryptoInterface::class, function () {
if (function_exists('openssl_cms_verify') && !config('crypto.force_process_spawn', false)) {
return new Signature\NativeService(tempFileService: app(TempFileInterface::class));
} else {
return new Signature\ProcessSpawnService(tempFileService: app(TempFileInterface::class));
}
Factory::createSignatureCryptoService(
certificatePath: config('crypto.signature.x509_cert'),
certificateKeyPath: config('crypto.signature.x509_key'),
certificateKeyPass: config('crypto.signature.x509_pass'),
certificateChain: config('crypto.signature.x509_chain'),
forceProcessSpawn: config('crypto.force_process_spawn', false),
);
});
}

Expand Down
36 changes: 10 additions & 26 deletions src/Exceptions/CryptoException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,48 @@

class CryptoException extends \RuntimeException
{
/**
* @param string $msg
* @return CryptoException
*/
public static function encrypt(string $msg = ""): CryptoException
{
if (! empty($msg)) {
$msg = ": " . $msg;
}

return new self("Cannot encrypt data" . $msg);
return new self("cannot encrypt data" . $msg);
}

/**
* @param string $msg
* @return CryptoException
*/
public static function decrypt(string $msg = ""): CryptoException
{
if (! empty($msg)) {
$msg = ": " . $msg;
}

return new self("Cannot decrypt data" . $msg);
return new self("cannot decrypt data" . $msg);
}

/**
* @param string $msg
* @return CryptoException
*/
public static function sign(string $msg = ""): CryptoException
{
if (! empty($msg)) {
$msg = ": " . $msg;
}

return new self("Cannot sign data" . $msg);
return new self("cannot sign data" . $msg);
}

/**
* @param string $msg
* @return CryptoException
*/
public static function verify(string $msg = ""): CryptoException
{
if (! empty($msg)) {
$msg = ": " . $msg;
}

return new self("Cannot verify data" . $msg);
return new self("cannot verify data" . $msg);
}

/**
* @param string $path
* @return CryptoException
*/
public static function cannotReadFile(string $path): CryptoException
public static function cannotReadFile(?string $path): CryptoException
{
return new self(sprintf("Error while reading keyfile %s: file is not readable by user (try chmod 644)", $path));
if (!$path) {
return new self(sprintf("no keyfile has been specified"));
}

return new self(sprintf("error while reading keyfile %s: file is not readable by user (try chmod 644)", $path));
}
}
64 changes: 64 additions & 0 deletions src/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace MinVWS\Crypto\Laravel;

use MinVWS\Crypto\Laravel\Service\Cms;
use MinVWS\Crypto\Laravel\Service\Signature;
use MinVWS\Crypto\Laravel\Service\Sealbox\SealboxService;
use MinVWS\Crypto\Laravel\Service\TempFileService;

class Factory
{
public static function createCmsCryptoService(
array $encryptionCertPaths = [],
?string $decryptionCertPath = null,
?string $decryptionKeyPath = null,
bool $forceProcessSpawn = false
): CmsCryptoInterface {
if (function_exists('openssl_cms_encrypt') && !$forceProcessSpawn) {
$tmpFile = new TempFileService();

return new Cms\NativeService(
$encryptionCertPaths,
$decryptionCertPath,
$decryptionKeyPath,
$tmpFile
);
}

return new Cms\ProcessSpawnService(
$encryptionCertPaths,
$decryptionCertPath,
$decryptionKeyPath,
);
}

public static function createSealboxCryptoService(
?string $privKey = null,
?string $recipientPubKey = null
): SealboxCryptoInterface {
return new SealboxService($privKey, $recipientPubKey);
}

public static function createSignatureCryptoService(
?string $certificatePath = null,
?string $certificateKeyPath = null,
?string $certificateKeyPass = null,
?string $certificateChain = null,
bool $forceProcessSpawn = false
): SignatureCryptoInterface {
$args = [
$certificatePath,
$certificateKeyPath,
$certificateKeyPass,
$certificateChain,
app(TempFileInterface::class),
];

if (function_exists('openssl_cms_sign') && !$forceProcessSpawn) {
return new Signature\NativeService(...$args);
}

return new Signature\ProcessSpawnService(...$args);
}
}
34 changes: 19 additions & 15 deletions src/Service/Cms/NativeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use MinVWS\Crypto\Laravel\CmsCryptoInterface;
use MinVWS\Crypto\Laravel\Exceptions\CryptoException;
use MinVWS\Crypto\Laravel\Service\TempFileService;
use MinVWS\Crypto\Laravel\TempFileInterface;

class NativeService implements CmsCryptoInterface
Expand All @@ -17,40 +18,39 @@ class NativeService implements CmsCryptoInterface
* @var string Single certificate path used for decrypting the data. The data could be encrypted for multiple
* certs, but this software only will use this cert to (try to) decode.
*/
protected string $decryptionCertPath;
protected string|null $decryptionCertPath;

/** @var string Path to private key of the $decryptionCert certificate. Needed to decrypt the actual data. */
protected string $decryptionCertKeyPath;
protected string|null $decryptionCertKeyPath;

protected TempFileInterface $tempFileService;

/**
* @param array $encryptionCertsPath
* @param string $decryptionCertPath
* @param string $decryptionCertKeyPath
* @param TempFileInterface $tempFileService
*/
public function __construct(
array $encryptionCertsPath,
string $decryptionCertPath,
string $decryptionCertKeyPath,
TempFileInterface $tempFileService
array $encryptionCertsPath = [],
?string $decryptionCertPath = null,
?string $decryptionCertKeyPath = null,
TempFileInterface $tempFileService = null
) {
// All path names should be prefixed with file://
$paths = [];
foreach ($encryptionCertsPath as $p) {
$paths[] = "file://" . $p;
}
$this->encryptionCertsPath = $paths;
$this->decryptionCertPath = "file://" . $decryptionCertPath;
$this->decryptionCertKeyPath = "file://" . $decryptionCertKeyPath;
$this->tempFileService = $tempFileService;
$this->decryptionCertPath = $decryptionCertPath ? "file://" . $decryptionCertPath : null;
$this->decryptionCertKeyPath = $decryptionCertKeyPath ? "file://" . $decryptionCertKeyPath : null;

$this->tempFileService = $tempFileService ?? new TempFileService();
}

public function encrypt(string $plainText): string
{
$outFile = $inFile = null;

if (count($this->encryptionCertsPath) == 0) {
throw CryptoException::encrypt('cannot encrypt without providing at least one certificate');
}

try {
$inFile = $this->tempFileService->createTempFileWithContent($plainText);
$inFilePath = $this->tempFileService->getTempFilePath($inFile);
Expand Down Expand Up @@ -87,6 +87,10 @@ public function decrypt(string $cipherText): string
{
$outFile = $inFile = null;

if ($this->decryptionCertPath === null || $this->decryptionCertKeyPath === null) {
throw CryptoException::decrypt("no decryption certificate or key provided");
}

if (!is_readable($this->decryptionCertPath)) {
throw CryptoException::cannotReadFile($this->decryptionCertPath);
}
Expand Down
37 changes: 24 additions & 13 deletions src/Service/Cms/ProcessSpawnService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@

use MinVWS\Crypto\Laravel\CmsCryptoInterface;
use MinVWS\Crypto\Laravel\Exceptions\CryptoException;
use MinVWS\Crypto\Laravel\Service\TempFileService;
use MinVWS\Crypto\Laravel\TempFileInterface;
use Symfony\Component\Process\Process;

class ProcessSpawnService implements CmsCryptoInterface
{
/**
* @var string[] Paths to certificates that are used to encrypt the data. The privkey of any of these certs can
* decrypt the data. Useful when you want to decrypt the same data at multiple places. */
protected $encryptionCertsPath;
protected array $encryptionCertsPath;

/**
* @var string Path to single certificate used for decrypting the data. The data could be encrypted for multiple
* certs, but this software only will use this cert to (try to) decode.
* @var string|null Path to single certificate used for decrypting the data. The data could be encrypted for
* multiple certs, but this software only will use this cert to (try to) decode.
*/
protected $decryptionCertPath;
protected ?string $decryptionCertPath;

/** @var string The path to the private key of $decryptionCert cert. Needed to decrypt the actual data. */
protected $decryptionCertKeyPath;
/** @var string|null The path to the private key of $decryptionCert cert. Needed to decrypt the actual data. */
protected ?string $decryptionCertKeyPath;

/**
* @param array $encryptionCertsPath
* @param string $decryptionCertPath
* @param string $decryptionCertKeyPath
*/
public function __construct(array $encryptionCertsPath, string $decryptionCertPath, string $decryptionCertKeyPath)
{
public function __construct(
array $encryptionCertsPath = [],
?string $decryptionCertPath = null,
?string $decryptionCertKeyPath = null,
) {
$this->encryptionCertsPath = $encryptionCertsPath;
$this->decryptionCertPath = $decryptionCertPath;
$this->decryptionCertKeyPath = $decryptionCertKeyPath;
Expand All @@ -41,6 +41,10 @@ public function __construct(array $encryptionCertsPath, string $decryptionCertPa
*/
public function encrypt(string $plainText): string
{
if (count($this->encryptionCertsPath) == 0) {
throw CryptoException::encrypt('cannot encrypt without providing at least one certificate');
}

$args = array_merge(
['openssl', 'cms', '-stream', '-encrypt', '-aes-256-cbc', '-outform', 'PEM'],
$this->encryptionCertsPath
Expand All @@ -63,6 +67,13 @@ public function encrypt(string $plainText): string
*/
public function decrypt(string $cipherText): string
{
if ($this->decryptionCertPath === null || $this->decryptionCertKeyPath === null) {
throw CryptoException::decrypt("no decryption certificate or key provided");
}

if (!is_readable($this->decryptionCertPath)) {
throw CryptoException::cannotReadFile($this->decryptionCertPath);
}
if (!is_readable($this->decryptionCertKeyPath)) {
throw CryptoException::cannotReadFile($this->decryptionCertKeyPath);
}
Expand Down
Loading

0 comments on commit 5c8a8b7

Please sign in to comment.