mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Drop support for gnupg pecl extension as it fails with "no passphrase" issues
This commit is contained in:
parent
3d176d4e4f
commit
68ed7cafae
7 changed files with 536 additions and 492 deletions
|
|
@ -24,7 +24,7 @@ trait Pgp
|
||||||
|
|
||||||
$GPG = $this->GnuPG();
|
$GPG = $this->GnuPG();
|
||||||
if ($GPG) {
|
if ($GPG) {
|
||||||
$keys = $GPG->keyInfo('');
|
$keys = $GPG->allKeysInfo('');
|
||||||
foreach ($keys['public'] as $key) {
|
foreach ($keys['public'] as $key) {
|
||||||
$key = $GPG->export($key['subkeys'][0]['fingerprint'] ?: $key['subkeys'][0]['keyid']);
|
$key = $GPG->export($key['subkeys'][0]['fingerprint'] ?: $key['subkeys'][0]['keyid']);
|
||||||
if ($key) {
|
if ($key) {
|
||||||
|
|
@ -47,7 +47,7 @@ trait Pgp
|
||||||
/**
|
/**
|
||||||
* @throws \MailSo\RuntimeException
|
* @throws \MailSo\RuntimeException
|
||||||
*/
|
*/
|
||||||
public function GnuPG() : ?GnuPG
|
public function GnuPG() : ?\SnappyMail\PGP\PGPInterface
|
||||||
{
|
{
|
||||||
$oAccount = $this->getMainAccountFromToken();
|
$oAccount = $this->getMainAccountFromToken();
|
||||||
if (!$oAccount) {
|
if (!$oAccount) {
|
||||||
|
|
@ -97,10 +97,6 @@ trait Pgp
|
||||||
$homedir = $tmpdir;
|
$homedir = $tmpdir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (104 <= \strlen($homedir . '/S.gpg-agent.extra')) {
|
|
||||||
throw new \Exception("socket name for '{$homedir}/S.gpg-agent.extra' is too long");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return GnuPG::getInstance($homedir);
|
return GnuPG::getInstance($homedir);
|
||||||
|
|
@ -160,7 +156,7 @@ trait Pgp
|
||||||
public function DoGnupgGetKeys() : array
|
public function DoGnupgGetKeys() : array
|
||||||
{
|
{
|
||||||
$GPG = $this->GnuPG();
|
$GPG = $this->GnuPG();
|
||||||
return $this->DefaultResponse($GPG ? $GPG->keyInfo('') : false);
|
return $this->DefaultResponse($GPG ? $GPG->allKeysInfo('') : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DoGnupgExportKey() : array
|
public function DoGnupgExportKey() : array
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
namespace SnappyMail\GPG;
|
namespace SnappyMail\GPG;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
abstract class Base
|
abstract class Base
|
||||||
{
|
{
|
||||||
const
|
const
|
||||||
|
|
@ -64,7 +66,13 @@ abstract class Base
|
||||||
|
|
||||||
function __construct(string $homedir)
|
function __construct(string $homedir)
|
||||||
{
|
{
|
||||||
$this->options['homedir'] = \rtrim($homedir, '/\\');
|
$homedir = \rtrim($homedir, '/\\');
|
||||||
|
// BSD 4.4 max length
|
||||||
|
if (104 <= \strlen($homedir . '/S.gpg-agent.extra')) {
|
||||||
|
throw new \Exception('Socket name for S.gpg-agent.extra is too long');
|
||||||
|
}
|
||||||
|
$this->options['homedir'] = $homedir;
|
||||||
|
// \putenv("GNUPGHOME={$homedir}");
|
||||||
}
|
}
|
||||||
|
|
||||||
function __destruct()
|
function __destruct()
|
||||||
|
|
@ -169,22 +177,13 @@ abstract class Base
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports a public key
|
* Exports a public or private key
|
||||||
*/
|
*/
|
||||||
public function export(string $fingerprint) /*: string|false*/
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports a private key
|
|
||||||
*/
|
|
||||||
public function exportPrivateKey(string $fingerprint) /*: string|false*/
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a key
|
* Imports a key
|
||||||
*/
|
*/
|
||||||
|
|
@ -209,12 +208,11 @@ abstract class Base
|
||||||
/**
|
/**
|
||||||
* Returns an array with information about all keys that matches the given pattern
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
*/
|
*/
|
||||||
public function keyInfo(string $pattern, int $private = 0) : array
|
public function keyInfo(string $pattern, bool $private = false) : array
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the mode for error_reporting
|
* Sets the mode for error_reporting
|
||||||
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
||||||
|
|
@ -284,7 +282,7 @@ abstract class Base
|
||||||
/**
|
/**
|
||||||
* Add a key for decryption
|
* Add a key for decryption
|
||||||
*/
|
*/
|
||||||
public function addDecryptKey(string $fingerprint, \SnappyMail\SensitiveString $passphrase) : bool
|
public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool
|
||||||
{
|
{
|
||||||
$this->decryptKeys[$fingerprint] = $passphrase;
|
$this->decryptKeys[$fingerprint] = $passphrase;
|
||||||
// $this->decryptKeys[\substr($fingerprint, -16)] = $passphrase;
|
// $this->decryptKeys[\substr($fingerprint, -16)] = $passphrase;
|
||||||
|
|
@ -303,7 +301,7 @@ abstract class Base
|
||||||
/**
|
/**
|
||||||
* Add a key for signing
|
* Add a key for signing
|
||||||
*/
|
*/
|
||||||
public function addSignKey(string $fingerprint, \SnappyMail\SensitiveString $passphrase) : bool
|
public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool
|
||||||
{
|
{
|
||||||
$this->signKeys[$fingerprint] = $passphrase;
|
$this->signKeys[$fingerprint] = $passphrase;
|
||||||
// $this->signKeys[\substr($fingerprint, -16)] = $passphrase;
|
// $this->signKeys[\substr($fingerprint, -16)] = $passphrase;
|
||||||
|
|
@ -350,7 +348,7 @@ abstract class Base
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addPassphrase($keyId, \SnappyMail\SensitiveString $passphrase)
|
public function addPassphrase($keyId, SensitiveString $passphrase)
|
||||||
{
|
{
|
||||||
$this->passphrases[$keyId] = $passphrase;
|
$this->passphrases[$keyId] = $passphrase;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
@ -358,10 +356,11 @@ abstract class Base
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the armored output
|
* Toggle the armored output
|
||||||
|
* When true the output is ASCII
|
||||||
*/
|
*/
|
||||||
public function setArmor(int $armor = 1) : bool
|
public function setArmor(bool $armor = true) : bool
|
||||||
{
|
{
|
||||||
$this->armor = !!$armor;
|
$this->armor = $armor;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
namespace SnappyMail\GPG;
|
namespace SnappyMail\GPG;
|
||||||
|
|
||||||
class PGP extends Base
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
|
class PGP extends Base implements \SnappyMail\PGP\PGPInterface
|
||||||
{
|
{
|
||||||
private
|
private
|
||||||
$_message,
|
$_message,
|
||||||
|
|
@ -137,6 +139,7 @@ class PGP extends Base
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
}
|
}
|
||||||
|
// \rewind($fp);
|
||||||
return $this->_decrypt($fp, $output);
|
return $this->_decrypt($fp, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,6 +218,7 @@ class PGP extends Base
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
}
|
}
|
||||||
|
\rewind($fp);
|
||||||
return $this->_encrypt($fp, $output);
|
return $this->_encrypt($fp, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,9 +238,9 @@ class PGP extends Base
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _exportKey($keyId, $private = false)
|
protected function _exportKey($keyId, bool $private = false)
|
||||||
{
|
{
|
||||||
$keys = $this->keyInfo($keyId, $private ? 1 : 0);
|
$keys = $this->keyInfo($keyId, $private);
|
||||||
if (!$keys) {
|
if (!$keys) {
|
||||||
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||||
}
|
}
|
||||||
|
|
@ -252,21 +256,19 @@ class PGP extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports a public key
|
* Exports a public or private key
|
||||||
*/
|
*/
|
||||||
public function export(string $fingerprint) /*: string|false*/
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
|
// \SnappyMail\Log::debug('GnuPG', "export({$fingerprint}, {$passphrase})");
|
||||||
|
if (null !== $passphrase) {
|
||||||
|
return $this
|
||||||
|
->addPassphrase($fingerprint, $passphrase)
|
||||||
|
->_exportKey($fingerprint, true);
|
||||||
|
}
|
||||||
return $this->_exportKey($fingerprint);
|
return $this->_exportKey($fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports a private key
|
|
||||||
*/
|
|
||||||
public function exportPrivateKey(string $fingerprint) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->_exportKey($fingerprint, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the errortext, if a function fails
|
* Returns the errortext, if a function fails
|
||||||
*/
|
*/
|
||||||
|
|
@ -296,8 +298,13 @@ class PGP extends Base
|
||||||
* Also saves revocation certificate in {homedir}/openpgp-revocs.d/
|
* Also saves revocation certificate in {homedir}/openpgp-revocs.d/
|
||||||
* https://www.gnupg.org/documentation/manuals/gnupg/OpenPGP-Key-Management.html
|
* https://www.gnupg.org/documentation/manuals/gnupg/OpenPGP-Key-Management.html
|
||||||
*/
|
*/
|
||||||
public function generateKey(PGPKeySettings $settings) /*: string|false*/
|
public function generateKey(string $uid, SensitiveString $passphrase) /*: string|false*/
|
||||||
{
|
{
|
||||||
|
$settings = new PGPKeySettings;
|
||||||
|
$settings->name = $uid;
|
||||||
|
$settings->email = $uid;
|
||||||
|
$settings->passphrase = $passphrase;
|
||||||
|
|
||||||
$arguments = [
|
$arguments = [
|
||||||
'--batch',
|
'--batch',
|
||||||
'--yes',
|
'--yes',
|
||||||
|
|
@ -421,13 +428,13 @@ class PGP extends Base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteKey(string $keyId, bool $private)
|
public function deleteKey(string $keyId, bool $private) : bool
|
||||||
{
|
{
|
||||||
$key = $this->keyInfo($keyId, $private ? 1 : 0);
|
$key = $this->keyInfo($keyId, $private);
|
||||||
if (!$key) {
|
if (!$key) {
|
||||||
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||||
}
|
}
|
||||||
// if (!$private && $this->keyInfo($keyId, 1)) {
|
// if (!$private && $this->keyInfo($keyId, true)) {
|
||||||
// throw new \Exception('Delete private key first: ' . $keyId);
|
// throw new \Exception('Delete private key first: ' . $keyId);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
@ -448,7 +455,7 @@ class PGP extends Base
|
||||||
/**
|
/**
|
||||||
* Returns an array with information about all keys that matches the given pattern
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
*/
|
*/
|
||||||
public function keyInfo(string $pattern, int $private = 0) : array
|
public function keyInfo(string $pattern, bool $private = false) : array
|
||||||
{
|
{
|
||||||
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
||||||
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
||||||
|
|
@ -572,6 +579,30 @@ class PGP extends Base
|
||||||
return $keys;
|
return $keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
|
*/
|
||||||
|
public function allKeysInfo(string $pattern) : array
|
||||||
|
{
|
||||||
|
$keys = [
|
||||||
|
'public' => [],
|
||||||
|
'private' => []
|
||||||
|
];
|
||||||
|
// Public
|
||||||
|
foreach (($this->keyinfo($pattern) ?: []) as $key) {
|
||||||
|
$key['can_verify'] = $key['can_sign'];
|
||||||
|
unset($key['can_sign']);
|
||||||
|
$keys['public'][] = $key;
|
||||||
|
}
|
||||||
|
// Private, read https://github.com/php-gnupg/php-gnupg/issues/5
|
||||||
|
foreach (($this->keyinfo($pattern, 1) ?: []) as $key) {
|
||||||
|
$key['can_decrypt'] = $key['can_encrypt'];
|
||||||
|
unset($key['can_encrypt']);
|
||||||
|
$keys['private'][] = $key;
|
||||||
|
}
|
||||||
|
return $keys;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the mode for error_reporting
|
* Sets the mode for error_reporting
|
||||||
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
||||||
|
|
@ -671,6 +702,7 @@ class PGP extends Base
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
}
|
}
|
||||||
|
\rewind($fp);
|
||||||
return $this->_sign($fp, $output);
|
return $this->_sign($fp, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -768,6 +800,7 @@ class PGP extends Base
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
throw new \Exception('Invalid stream resource');
|
||||||
}
|
}
|
||||||
|
// \rewind($fp);
|
||||||
return $this->_verify($fp, $signature);
|
return $this->_verify($fp, $signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace SnappyMail\GPG;
|
namespace SnappyMail\GPG;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
class SMIME extends Base
|
class SMIME extends Base
|
||||||
{
|
{
|
||||||
private
|
private
|
||||||
|
|
@ -235,9 +237,9 @@ class SMIME extends Base
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _exportKey($keyId, $private = false)
|
protected function _exportKey($keyId, bool $private = false)
|
||||||
{
|
{
|
||||||
$keys = $this->keyInfo($keyId, $private ? 1 : 0);
|
$keys = $this->keyInfo($keyId, $private);
|
||||||
if (!$keys) {
|
if (!$keys) {
|
||||||
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||||
}
|
}
|
||||||
|
|
@ -253,21 +255,18 @@ class SMIME extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports a public key
|
* Exports a public or private key
|
||||||
*/
|
*/
|
||||||
public function export(string $fingerprint) /*: string|false*/
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/
|
||||||
{
|
{
|
||||||
|
if ($passphrase) {
|
||||||
|
return $this
|
||||||
|
->addPassphrase($fingerprint, $passphrase)
|
||||||
|
->_exportKey($fingerprint, true);
|
||||||
|
}
|
||||||
return $this->_exportKey($fingerprint);
|
return $this->_exportKey($fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports a private key
|
|
||||||
*/
|
|
||||||
public function exportPrivateKey(string $fingerprint) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->_exportKey($fingerprint, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function _importKey($input) /*: array|false*/
|
protected function _importKey($input) /*: array|false*/
|
||||||
{
|
{
|
||||||
$arguments = ['--import'];
|
$arguments = ['--import'];
|
||||||
|
|
@ -334,7 +333,7 @@ class SMIME extends Base
|
||||||
|
|
||||||
public function deleteKey(string $keyId, bool $private)
|
public function deleteKey(string $keyId, bool $private)
|
||||||
{
|
{
|
||||||
$key = $this->keyInfo($keyId, $private ? 1 : 0);
|
$key = $this->keyInfo($keyId, $private);
|
||||||
if (!$key) {
|
if (!$key) {
|
||||||
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +358,7 @@ class SMIME extends Base
|
||||||
/**
|
/**
|
||||||
* Returns an array with information about all keys that matches the given pattern
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
*/
|
*/
|
||||||
public function keyInfo(string $pattern, int $private = 0) : array
|
public function keyInfo(string $pattern, bool $private = false) : array
|
||||||
{
|
{
|
||||||
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
||||||
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
||||||
|
|
|
||||||
|
|
@ -8,449 +8,26 @@ defined('GNUPG_SIG_MODE_CLEAR') || define('GNUPG_SIG_MODE_CLEAR', 2);
|
||||||
|
|
||||||
use SnappyMail\GPG\PGP as GPG;
|
use SnappyMail\GPG\PGP as GPG;
|
||||||
|
|
||||||
class GnuPG
|
abstract class GnuPG
|
||||||
{
|
{
|
||||||
private
|
|
||||||
$homedir,
|
|
||||||
// Instance of gnupg pecl extension https://www.php.net/gnupg
|
|
||||||
$GnuPG,
|
|
||||||
// Instance of \SnappyMail\GPG\PGP
|
|
||||||
$GPG;
|
|
||||||
|
|
||||||
function __construct(string $homedir)
|
|
||||||
{
|
|
||||||
$homedir = \rtrim($homedir, '/\\');
|
|
||||||
// BSD 4.4 max length
|
|
||||||
if (104 <= \strlen($homedir . '/S.gpg-agent.extra')) {
|
|
||||||
throw new \Exception('socket name for S.gpg-agent.extra is too long');
|
|
||||||
}
|
|
||||||
$this->homedir = $homedir;
|
|
||||||
// \putenv("GNUPGHOME={$homedir}");
|
|
||||||
|
|
||||||
if (\class_exists('gnupg') && \version_compare(\phpversion('gnupg'), '1.5', '>=')) {
|
|
||||||
$this->GnuPG = new \gnupg([
|
|
||||||
// It is the file name of the executable program implementing this protocol which is usually path of the gpg executable.
|
|
||||||
// 'file_name' => '/usr/bin/gpg',
|
|
||||||
// It is the directory name of the configuration directory. It also overrides GNUPGHOME environment variable that is used for the same purpose.
|
|
||||||
'home_dir' => $homedir
|
|
||||||
]);
|
|
||||||
// Output is ASCII
|
|
||||||
$this->GnuPG->setarmor(1);
|
|
||||||
\SnappyMail\Log::info('GnuPG', 'Using PECL');
|
|
||||||
} else {
|
|
||||||
$this->getGPG();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
$conf = "{$homedir}/gpg-agent.conf";
|
|
||||||
if (!\file_exists($conf)) {
|
|
||||||
\file_put_contents($conf, 'default-cache-ttl 1');
|
|
||||||
}
|
|
||||||
$conf = "{$homedir}/gpg.conf";
|
|
||||||
if (!\file_exists($conf)) {
|
|
||||||
\file_put_contents($conf, "batch\nno-comments");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
function __destruct()
|
|
||||||
{
|
|
||||||
$this->clearDecryptKeys();
|
|
||||||
$this->clearSignKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function isSupported() : bool
|
public static function isSupported() : bool
|
||||||
{
|
{
|
||||||
return \class_exists('gnupg') || GPG::isSupported();
|
return GPG::isSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static $instance;
|
private static $instance;
|
||||||
public static function getInstance(string $homedir) : ?self
|
public static function getInstance(string $homedir) : ?PGPInterface
|
||||||
{
|
{
|
||||||
if (!static::$instance) {
|
if (!static::$instance) {
|
||||||
static::$instance = new self($homedir);
|
if (GPG::isSupported()) {
|
||||||
|
static::$instance = new GPG($homedir);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
else if (PECL::isSupported()) {
|
||||||
|
static::$instance = new PECL($homedir);
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
return static::$instance;
|
return static::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handler()
|
|
||||||
{
|
|
||||||
return $this->GnuPG ?: $this->GPG;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function gnupgError()
|
|
||||||
{
|
|
||||||
$error = $this->GnuPG ? $this->GnuPG->geterrorinfo() : null;
|
|
||||||
if ($error) {
|
|
||||||
throw new \Exception("{$error['gpgme_source']} {$error['generic_message']}. {$error['gpgme_message']}", $error['gpgme_code']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getGPG(bool $throw = true) : ?GPG
|
|
||||||
{
|
|
||||||
if (!$this->GPG) {
|
|
||||||
if (GPG::isSupported()) {
|
|
||||||
$this->GPG = new GPG($this->homedir);
|
|
||||||
\SnappyMail\Log::info('GnuPG', 'Using ' . GPG::class);
|
|
||||||
} else if ($throw) {
|
|
||||||
throw new \Exception('GnuPG not supported');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->GPG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a key for decryption
|
|
||||||
*/
|
|
||||||
public function addDecryptKey(string $fingerprint, \SnappyMail\SensitiveString $passphrase) : bool
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->adddecryptkey($fingerprint, \strval($passphrase)) || $this->gnupgError()
|
|
||||||
: $this->GPG->addDecryptKey($fingerprint, $passphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a key for encryption
|
|
||||||
*/
|
|
||||||
public function addEncryptKey(string $fingerprint) : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->addencryptkey($fingerprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a key for signing
|
|
||||||
*/
|
|
||||||
public function addSignKey(string $fingerprint, \SnappyMail\SensitiveString $passphrase) : bool
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->addsignkey($fingerprint, \strval($passphrase)) || $this->gnupgError()
|
|
||||||
: $this->GPG->addSignKey($fingerprint, $passphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all keys which were set for decryption before
|
|
||||||
*/
|
|
||||||
public function clearDecryptKeys() : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->cleardecryptkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all keys which were set for encryption before
|
|
||||||
*/
|
|
||||||
public function clearEncryptKeys() : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->clearencryptkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all keys which were set for signing before
|
|
||||||
*/
|
|
||||||
public function clearSignKeys() : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->clearsignkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a given text
|
|
||||||
*/
|
|
||||||
public function decrypt(string $text) /*: string|false */
|
|
||||||
{
|
|
||||||
if ($this->GnuPG) {
|
|
||||||
$result = $this->GnuPG->decrypt($text);
|
|
||||||
(false === $result) && $this->gnupgError();
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
return $this->GPG->decrypt($text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a given file
|
|
||||||
*/
|
|
||||||
public function decryptFile(string $filename) /*: string|false */
|
|
||||||
{
|
|
||||||
if ($this->GnuPG) {
|
|
||||||
$result = $this->GnuPG->decrypt(\file_get_contents($filename));
|
|
||||||
(false === $result) && $this->gnupgError();
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
return $this->GPG->decryptFile($filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a given resource
|
|
||||||
*/
|
|
||||||
public function decryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false */
|
|
||||||
{
|
|
||||||
if (!$fp || !\is_resource($fp)) {
|
|
||||||
throw new \Exception('Invalid stream resource');
|
|
||||||
}
|
|
||||||
if ($this->GnuPG) {
|
|
||||||
$result = $this->GnuPG->decrypt(\stream_get_contents($fp));
|
|
||||||
(false === $result) && $this->gnupgError();
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
return $this->GPG->decryptStream($fp, $output);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts and verifies a given text
|
|
||||||
*/
|
|
||||||
public function decryptVerify(string $text, string &$plaintext) /*: array|false*/
|
|
||||||
{
|
|
||||||
if ($this->GnuPG) {
|
|
||||||
$result = $this->GnuPG->decryptverify($text, $plaintext);
|
|
||||||
(false === $result) && $this->gnupgError();
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
return $this->GPG->decryptverify($text, $plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts and verifies a given file
|
|
||||||
*/
|
|
||||||
public function decryptVerifyFile(string $filename, string &$plaintext) /*: array|false*/
|
|
||||||
{
|
|
||||||
if ($this->GnuPG) {
|
|
||||||
$result = $this->GnuPG->decryptverify(\file_get_contents($filename), $plaintext);
|
|
||||||
(false === $result) && $this->gnupgError();
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
return $this->GPG->decryptverifyFile($filename, $plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteKey(string $keyId, bool $private) : bool
|
|
||||||
{
|
|
||||||
return $this->getGPG()->deleteKey($keyId, $private);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a given text
|
|
||||||
*/
|
|
||||||
public function encrypt(string $plaintext) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->encrypt($plaintext) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->encrypt($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a given text
|
|
||||||
*/
|
|
||||||
public function encryptFile(string $filename) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->encrypt(\file_get_contents($filename)) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->encryptFile($filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function encryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false*/
|
|
||||||
{
|
|
||||||
\rewind($fp);
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->encrypt(\stream_get_contents($fp)) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->encryptStream($fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports a key
|
|
||||||
*/
|
|
||||||
public function export(string $fingerprint, ?\SnappyMail\SensitiveString $passphrase = null) /*: string|false*/
|
|
||||||
{
|
|
||||||
if ($passphrase) {
|
|
||||||
return $this->getGPG()
|
|
||||||
->addPassphrase($fingerprint, $passphrase)
|
|
||||||
->exportPrivateKey($fingerprint);
|
|
||||||
}
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->export($fingerprint) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->export($fingerprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the engine info
|
|
||||||
*/
|
|
||||||
public function getEngineInfo() : array
|
|
||||||
{
|
|
||||||
return $this->handler()->getengineinfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the errortext, if a function fails
|
|
||||||
*/
|
|
||||||
public function getError() /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->handler()->geterror();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the error info
|
|
||||||
*/
|
|
||||||
public function getErrorInfo() : array
|
|
||||||
{
|
|
||||||
return $this->handler()->geterrorinfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently active protocol for all operations
|
|
||||||
*/
|
|
||||||
public function getProtocol() : int
|
|
||||||
{
|
|
||||||
return $this->handler()->getprotocol();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a key
|
|
||||||
*/
|
|
||||||
public function generateKey(string $uid, \SnappyMail\SensitiveString $passphrase) /*: string|false*/
|
|
||||||
{
|
|
||||||
$GPG = $this->getGPG(false);
|
|
||||||
return $GPG ? $GPG->generateKey($uid, $passphrase) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports a key
|
|
||||||
*/
|
|
||||||
public function import(string $keydata) /*: array|false*/
|
|
||||||
{
|
|
||||||
return $this->handler()->import($keydata);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports a key
|
|
||||||
*/
|
|
||||||
public function importFile(string $filename) /*: array|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->import(\file_get_contents($filename)) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->importFile($filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array with information about all keys that matches the given pattern
|
|
||||||
*/
|
|
||||||
public function keyInfo(string $pattern) : array
|
|
||||||
{
|
|
||||||
$keys = [
|
|
||||||
'public' => [],
|
|
||||||
'private' => []
|
|
||||||
];
|
|
||||||
// Public
|
|
||||||
foreach (($this->handler()->keyinfo($pattern) ?: []) as $key) {
|
|
||||||
$key['can_verify'] = $key['can_sign'];
|
|
||||||
unset($key['can_sign']);
|
|
||||||
$keys['public'][] = $key;
|
|
||||||
}
|
|
||||||
// Private, read https://github.com/php-gnupg/php-gnupg/issues/5
|
|
||||||
foreach (($this->handler()->keyinfo($pattern, 1) ?: []) as $key) {
|
|
||||||
$key['can_decrypt'] = $key['can_encrypt'];
|
|
||||||
unset($key['can_encrypt']);
|
|
||||||
$keys['private'][] = $key;
|
|
||||||
}
|
|
||||||
return $keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle armored output
|
|
||||||
* When true the output is ASCII
|
|
||||||
*/
|
|
||||||
public function setArmor(bool $armor = true) : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->setarmor($armor ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the mode for error_reporting
|
|
||||||
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
|
||||||
* By default GNUPG_ERROR_SILENT is used.
|
|
||||||
*/
|
|
||||||
public function setErrorMode(int $errormode) : void
|
|
||||||
{
|
|
||||||
$this->handler()->seterrormode($errormode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the mode for signing
|
|
||||||
* GNUPG_SIG_MODE_NORMAL, GNUPG_SIG_MODE_DETACH and GNUPG_SIG_MODE_CLEAR.
|
|
||||||
* By default GNUPG_SIG_MODE_CLEAR
|
|
||||||
*/
|
|
||||||
public function setSignMode(int $signmode) : bool
|
|
||||||
{
|
|
||||||
return $this->handler()->setsignmode($signmode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs a given text
|
|
||||||
*/
|
|
||||||
public function sign(string $plaintext) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->sign($plaintext) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->sign($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs a given file
|
|
||||||
*/
|
|
||||||
public function signFile(string $filename) /*: string|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->sign(\file_get_contents($filename)) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->signFile($filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs a given file
|
|
||||||
*/
|
|
||||||
public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/
|
|
||||||
{
|
|
||||||
\rewind($fp);
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->sign(\stream_get_contents($fp)) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->signStream($fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies a signed text
|
|
||||||
*/
|
|
||||||
public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/
|
|
||||||
{
|
|
||||||
$result = $this->GnuPG
|
|
||||||
? $this->GnuPG->verify($signed_text, $signature ?: false, $plaintext) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->verify($signed_text, $signature, $plaintext);
|
|
||||||
if (!$result) {
|
|
||||||
if ($this->GnuPG) {
|
|
||||||
\SnappyMail\Log::notice('GnuPG', 'gnupg_verify() failed: ' . $this->GnuPG->geterror());
|
|
||||||
\SnappyMail\Log::info('GnuPG', \print_r($this->GnuPG->geterrorinfo(),1));
|
|
||||||
} else {
|
|
||||||
\SnappyMail\Log::notice('GPG', 'GPG->verify() failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies a signed file
|
|
||||||
*/
|
|
||||||
public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/
|
|
||||||
{
|
|
||||||
return $this->GnuPG
|
|
||||||
? $this->GnuPG->verify(\file_get_contents($filename), $signature, $plaintext) ?: $this->gnupgError()
|
|
||||||
: $this->GPG->verifyFile($filename, $signature, $plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies a given resource
|
|
||||||
*/
|
|
||||||
public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: array|false */
|
|
||||||
{
|
|
||||||
if (!$fp || !\is_resource($fp)) {
|
|
||||||
throw new \Exception('Invalid stream resource');
|
|
||||||
}
|
|
||||||
// return $this->GnuPG
|
|
||||||
// ? $this->GnuPG->verify(\stream_get_contents($fp), $signature, $plaintext) ?: $this->gnupgError()
|
|
||||||
// : $this->GPG->verifyStream($fp, $signature, $plaintext);
|
|
||||||
return $this->getGPG()->verifyStream($fp, $signature, $plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
397
snappymail/v/0.0.0/app/libraries/snappymail/pgp/pecl.php
Normal file
397
snappymail/v/0.0.0/app/libraries/snappymail/pgp/pecl.php
Normal file
|
|
@ -0,0 +1,397 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SnappyMail\PGP;
|
||||||
|
|
||||||
|
defined('GNUPG_SIG_MODE_NORMAL') || define('GNUPG_SIG_MODE_NORMAL', 0);
|
||||||
|
defined('GNUPG_SIG_MODE_DETACH') || define('GNUPG_SIG_MODE_DETACH', 1);
|
||||||
|
defined('GNUPG_SIG_MODE_CLEAR') || define('GNUPG_SIG_MODE_CLEAR', 2);
|
||||||
|
|
||||||
|
use SnappyMail\GPG\PGP as GPG;
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
|
class PECL implements \SnappyMail\PGP\PGPInterface
|
||||||
|
{
|
||||||
|
private
|
||||||
|
$homedir,
|
||||||
|
// Instance of gnupg pecl extension https://www.php.net/gnupg
|
||||||
|
$GnuPG,
|
||||||
|
// Instance of \SnappyMail\GPG\PGP
|
||||||
|
$GPG;
|
||||||
|
|
||||||
|
function __construct(string $homedir)
|
||||||
|
{
|
||||||
|
$homedir = \rtrim($homedir, '/\\');
|
||||||
|
// BSD 4.4 max length
|
||||||
|
if (104 <= \strlen($homedir . '/S.gpg-agent.extra')) {
|
||||||
|
throw new \Exception('Socket name for S.gpg-agent.extra is too long');
|
||||||
|
}
|
||||||
|
$this->homedir = $homedir;
|
||||||
|
// \putenv("GNUPGHOME={$homedir}");
|
||||||
|
|
||||||
|
$this->GnuPG = new \gnupg([
|
||||||
|
// It is the file name of the executable program implementing this protocol which is usually path of the gpg executable.
|
||||||
|
// 'file_name' => '/usr/bin/gpg',
|
||||||
|
// It is the directory name of the configuration directory. It also overrides GNUPGHOME environment variable that is used for the same purpose.
|
||||||
|
'home_dir' => $homedir
|
||||||
|
]);
|
||||||
|
// Output is ASCII
|
||||||
|
$this->GnuPG->setarmor(1);
|
||||||
|
/*
|
||||||
|
$conf = "{$homedir}/gpg-agent.conf";
|
||||||
|
if (!\file_exists($conf)) {
|
||||||
|
\file_put_contents($conf, 'default-cache-ttl 1');
|
||||||
|
}
|
||||||
|
$conf = "{$homedir}/gpg.conf";
|
||||||
|
if (!\file_exists($conf)) {
|
||||||
|
\file_put_contents($conf, "batch\nno-comments");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function __destruct()
|
||||||
|
{
|
||||||
|
$this->GnuPG->cleardecryptkeys();
|
||||||
|
$this->GnuPG->clearsignkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isSupported() : bool
|
||||||
|
{
|
||||||
|
return \class_exists('gnupg') && \version_compare(\phpversion('gnupg'), '1.5', '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gnupgError()
|
||||||
|
{
|
||||||
|
$error = $this->GnuPG->geterrorinfo();
|
||||||
|
if ($error) {
|
||||||
|
throw new \Exception("{$error['gpgme_source']} {$error['generic_message']}. {$error['gpgme_message']}", $error['gpgme_code']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGPG(bool $throw = true) : ?GPG
|
||||||
|
{
|
||||||
|
if (!$this->GPG) {
|
||||||
|
if (GPG::isSupported()) {
|
||||||
|
$this->GPG = new GPG($this->homedir);
|
||||||
|
} else if ($throw) {
|
||||||
|
throw new \Exception('GPG not supported');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->GPG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key for decryption
|
||||||
|
*/
|
||||||
|
public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool
|
||||||
|
{
|
||||||
|
// \SnappyMail\Log::debug('GnuPG', "addDecryptKey({$fingerprint}, {$passphrase})");
|
||||||
|
return $this->GnuPG->adddecryptkey($fingerprint, \strval($passphrase)) || $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key for encryption
|
||||||
|
*/
|
||||||
|
public function addEncryptKey(string $fingerprint) : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->addencryptkey($fingerprint) || $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key for signing
|
||||||
|
*/
|
||||||
|
public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool
|
||||||
|
{
|
||||||
|
// \SnappyMail\Log::debug('GnuPG', "addSignKey({$fingerprint}, {$passphrase})");
|
||||||
|
return $this->GnuPG->addsignkey($fingerprint, \strval($passphrase)) || $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all keys which were set for decryption before
|
||||||
|
*/
|
||||||
|
public function clearDecryptKeys() : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->cleardecryptkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all keys which were set for encryption before
|
||||||
|
*/
|
||||||
|
public function clearEncryptKeys() : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->clearencryptkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all keys which were set for signing before
|
||||||
|
*/
|
||||||
|
public function clearSignKeys() : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->clearsignkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts a given text
|
||||||
|
*/
|
||||||
|
public function decrypt(string $text) /*: string|false */
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->decrypt($text);
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts a given file
|
||||||
|
*/
|
||||||
|
public function decryptFile(string $filename) /*: string|false */
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->decrypt(\file_get_contents($filename));
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts a given resource
|
||||||
|
*/
|
||||||
|
public function decryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false */
|
||||||
|
{
|
||||||
|
if (!$fp || !\is_resource($fp)) {
|
||||||
|
throw new \Exception('Invalid stream resource');
|
||||||
|
}
|
||||||
|
// \rewind($fp);
|
||||||
|
$result = $this->GnuPG->decrypt(\stream_get_contents($fp));
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts and verifies a given text
|
||||||
|
*/
|
||||||
|
public function decryptVerify(string $text, string &$plaintext) /*: array|false*/
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->decryptverify($text, $plaintext);
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts and verifies a given file
|
||||||
|
*/
|
||||||
|
public function decryptVerifyFile(string $filename, string &$plaintext) /*: array|false*/
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->decryptverify(\file_get_contents($filename), $plaintext);
|
||||||
|
(false === $result) && $this->gnupgError();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteKey(string $keyId, bool $private) : bool
|
||||||
|
{
|
||||||
|
return $this->getGPG()->deleteKey($keyId, $private);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts a given text
|
||||||
|
*/
|
||||||
|
public function encrypt(string $plaintext) /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->encrypt($plaintext) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts a given text
|
||||||
|
*/
|
||||||
|
public function encryptFile(string $filename) /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->encrypt(\file_get_contents($filename)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function encryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false*/
|
||||||
|
{
|
||||||
|
\rewind($fp);
|
||||||
|
return $this->GnuPG->encrypt(\stream_get_contents($fp)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports a key
|
||||||
|
*/
|
||||||
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/
|
||||||
|
{
|
||||||
|
if (null !== $passphrase) {
|
||||||
|
return $this->getGPG()->export($fingerprint, $passphrase);
|
||||||
|
}
|
||||||
|
return $this->GnuPG->export($fingerprint) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the engine info
|
||||||
|
*/
|
||||||
|
public function getEngineInfo() : array
|
||||||
|
{
|
||||||
|
return $this->GnuPG->getengineinfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the errortext, if a function fails
|
||||||
|
*/
|
||||||
|
public function getError() /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->geterror();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error info
|
||||||
|
*/
|
||||||
|
public function getErrorInfo() : array
|
||||||
|
{
|
||||||
|
return $this->GnuPG->geterrorinfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently active protocol for all operations
|
||||||
|
*/
|
||||||
|
public function getProtocol() : int
|
||||||
|
{
|
||||||
|
return $this->GnuPG->getprotocol();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a key
|
||||||
|
*/
|
||||||
|
public function generateKey(string $uid, SensitiveString $passphrase) /*: string|false*/
|
||||||
|
{
|
||||||
|
$GPG = $this->getGPG(false);
|
||||||
|
return $GPG ? $GPG->generateKey($uid, $passphrase) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a key
|
||||||
|
*/
|
||||||
|
public function import(string $keydata) /*: array|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->import($keydata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a key
|
||||||
|
*/
|
||||||
|
public function importFile(string $filename) /*: array|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->import(\file_get_contents($filename)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function keyInfo(string $pattern, bool $private = false) : array
|
||||||
|
{
|
||||||
|
return $this->GnuPG->keyinfo($pattern, $private ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with information about all keys that matches the given pattern
|
||||||
|
*/
|
||||||
|
public function allKeysInfo(string $pattern) : array
|
||||||
|
{
|
||||||
|
$keys = [
|
||||||
|
'public' => [],
|
||||||
|
'private' => []
|
||||||
|
];
|
||||||
|
// Public
|
||||||
|
foreach (($this->GnuPG->keyinfo($pattern) ?: []) as $key) {
|
||||||
|
$key['can_verify'] = $key['can_sign'];
|
||||||
|
unset($key['can_sign']);
|
||||||
|
$keys['public'][] = $key;
|
||||||
|
}
|
||||||
|
// Private, read https://github.com/php-gnupg/php-gnupg/issues/5
|
||||||
|
foreach (($this->GnuPG->keyinfo($pattern, 1) ?: []) as $key) {
|
||||||
|
$key['can_decrypt'] = $key['can_encrypt'];
|
||||||
|
unset($key['can_encrypt']);
|
||||||
|
$keys['private'][] = $key;
|
||||||
|
}
|
||||||
|
return $keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle armored output
|
||||||
|
* When true the output is ASCII
|
||||||
|
*/
|
||||||
|
public function setArmor(bool $armor = true) : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->setarmor($armor ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the mode for error_reporting
|
||||||
|
* GNUPG_ERROR_WARNING, GNUPG_ERROR_EXCEPTION and GNUPG_ERROR_SILENT.
|
||||||
|
* By default GNUPG_ERROR_SILENT is used.
|
||||||
|
*/
|
||||||
|
public function setErrorMode(int $errormode) : void
|
||||||
|
{
|
||||||
|
$this->GnuPG->seterrormode($errormode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the mode for signing
|
||||||
|
* GNUPG_SIG_MODE_NORMAL, GNUPG_SIG_MODE_DETACH and GNUPG_SIG_MODE_CLEAR.
|
||||||
|
* By default GNUPG_SIG_MODE_CLEAR
|
||||||
|
*/
|
||||||
|
public function setSignMode(int $signmode) : bool
|
||||||
|
{
|
||||||
|
return $this->GnuPG->setsignmode($signmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a given text
|
||||||
|
*/
|
||||||
|
public function sign(string $plaintext) /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->sign($plaintext) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a given file
|
||||||
|
*/
|
||||||
|
public function signFile(string $filename) /*: string|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->sign(\file_get_contents($filename)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a given file
|
||||||
|
*/
|
||||||
|
public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/
|
||||||
|
{
|
||||||
|
\rewind($fp);
|
||||||
|
return $this->GnuPG->sign(\stream_get_contents($fp)) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a signed text
|
||||||
|
*/
|
||||||
|
public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/
|
||||||
|
{
|
||||||
|
$result = $this->GnuPG->verify($signed_text, $signature ?: false, $plaintext) ?: $this->gnupgError();
|
||||||
|
if (!$result) {
|
||||||
|
\SnappyMail\Log::notice('GnuPG', 'gnupg_verify() failed: ' . $this->GnuPG->geterror());
|
||||||
|
\SnappyMail\Log::info('GnuPG', \print_r($this->GnuPG->geterrorinfo(),1));
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a signed file
|
||||||
|
*/
|
||||||
|
public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/
|
||||||
|
{
|
||||||
|
return $this->GnuPG->verify(\file_get_contents($filename), $signature, $plaintext) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a given resource
|
||||||
|
*/
|
||||||
|
public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: array|false */
|
||||||
|
{
|
||||||
|
if (!$fp || !\is_resource($fp)) {
|
||||||
|
throw new \Exception('Invalid stream resource');
|
||||||
|
}
|
||||||
|
// \rewind($fp);
|
||||||
|
return $this->GnuPG->verify(\stream_get_contents($fp), $signature, $plaintext) ?: $this->gnupgError();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SnappyMail\PGP;
|
||||||
|
|
||||||
|
use SnappyMail\SensitiveString;
|
||||||
|
|
||||||
|
interface PGPInterface
|
||||||
|
{
|
||||||
|
public static function isSupported() : bool;
|
||||||
|
public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool;
|
||||||
|
public function addEncryptKey(string $fingerprint) : bool;
|
||||||
|
public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool;
|
||||||
|
public function clearDecryptKeys() : bool;
|
||||||
|
public function clearEncryptKeys() : bool;
|
||||||
|
public function clearSignKeys() : bool;
|
||||||
|
public function decrypt(string $text) /*: string|false */;
|
||||||
|
public function decryptFile(string $filename) /*: string|false */;
|
||||||
|
public function decryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false */;
|
||||||
|
public function decryptVerify(string $text, string &$plaintext) /*: array|false*/;
|
||||||
|
public function decryptVerifyFile(string $filename, string &$plaintext) /*: array|false*/;
|
||||||
|
public function deleteKey(string $keyId, bool $private) : bool;
|
||||||
|
public function encrypt(string $plaintext) /*: string|false*/;
|
||||||
|
public function encryptFile(string $filename) /*: string|false*/;
|
||||||
|
public function encryptStream(/*resource*/ $fp, /*string|resource*/ $output = null) /*: string|false*/;
|
||||||
|
public function export(string $fingerprint, ?SensitiveString $passphrase = null) /*: string|false*/;
|
||||||
|
public function getEngineInfo() : array;
|
||||||
|
public function getError() /*: string|false*/;
|
||||||
|
public function getErrorInfo() : array;
|
||||||
|
public function getProtocol() : int;
|
||||||
|
public function generateKey(string $uid, SensitiveString $passphrase) /*: string|false*/;
|
||||||
|
public function import(string $keydata) /*: array|false*/;
|
||||||
|
public function importFile(string $filename) /*: array|false*/;
|
||||||
|
public function allKeysInfo(string $pattern) : array;
|
||||||
|
public function setArmor(bool $armor = true) : bool;
|
||||||
|
public function setErrorMode(int $errormode) : void;
|
||||||
|
public function setSignMode(int $signmode) : bool;
|
||||||
|
public function sign(string $plaintext) /*: string|false*/;
|
||||||
|
public function signFile(string $filename) /*: string|false*/;
|
||||||
|
public function signStream($fp, /*string|resource*/ $output = null) /*: string|false*/;
|
||||||
|
public function verify(string $signed_text, string $signature, string &$plaintext = null) /*: array|false*/;
|
||||||
|
public function verifyFile(string $filename, string $signature, string &$plaintext = null) /*: array|false*/;
|
||||||
|
public function verifyStream(/*resource*/ $fp, string $signature, string &$plaintext = null) /*: array|false */;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue