GPG use pinentries for decrypt, sign and export

This commit is contained in:
the-djmaze 2024-03-04 10:14:14 +01:00
parent 7d485813e5
commit 464d22d69a
4 changed files with 62 additions and 36 deletions

View file

@ -43,10 +43,8 @@ abstract class Base
protected protected
$binary, $binary,
$version = '2.0', $version = '2.0',
$passphrases = [], $pinentries = [],
$signKeys = [],
$encryptKeys = [], $encryptKeys = [],
$decryptKeys = [],
// Create PEM encoded output // Create PEM encoded output
$armor = true, $armor = true,
@ -284,8 +282,7 @@ abstract class Base
*/ */
public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool
{ {
$this->decryptKeys[$fingerprint] = $passphrase; $this->addPinentry($fingerprint, $passphrase);
// $this->decryptKeys[\substr($fingerprint, -16)] = $passphrase;
return true; return true;
} }
@ -303,9 +300,15 @@ abstract class Base
*/ */
public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool
{ {
$this->signKeys[$fingerprint] = $passphrase; $this->addPinentry($fingerprint, $passphrase);
// $this->signKeys[\substr($fingerprint, -16)] = $passphrase; return true;
return false; }
public function clear() : bool
{
$this->clearPinentries();
$this->clearEncryptKeys();
return true;
} }
/** /**
@ -313,8 +316,7 @@ abstract class Base
*/ */
public function clearDecryptKeys() : bool public function clearDecryptKeys() : bool
{ {
$this->decryptKeys = []; return $this->clearPinentries();
return true;
} }
/** /**
@ -331,8 +333,7 @@ abstract class Base
*/ */
public function clearSignKeys() : bool public function clearSignKeys() : bool
{ {
$this->signKeys = []; return $this->clearPinentries();
return true;
} }
/** /**
@ -348,12 +349,36 @@ abstract class Base
]; ];
} }
public function addPassphrase($keyId, SensitiveString $passphrase) /**
* Add private key passphrase for decrypt, sign or export
* $keyId or fingerprint
*/
public function addPinentry(string $keyId, SensitiveString $passphrase)
{ {
$this->passphrases[$keyId] = $passphrase; /**
* Test first?
* gpg --dry-run --passwd <your-user-id>
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', [$keyId => $passphrase]));
$result = $this->exec([
'--dry-run',
'--passwd',
$fingerprint
]);
*/
$this->pinentries[$keyId] = $passphrase;
// $this->pinentries[\substr($keyId, -16)] = $passphrase;
return $this; return $this;
} }
/**
* Removes all keys which were set for decryption, signing and export
*/
public function clearPinentries() : bool
{
$this->pinentries = [];
return true;
}
/** /**
* Toggle the armored output * Toggle the armored output
* When true the output is ASCII * When true the output is ASCII

View file

@ -96,8 +96,8 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
$fclose = $this->setOutput($output); $fclose = $this->setOutput($output);
if ($this->decryptKeys) { if ($this->pinentries) {
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->decryptKeys)); $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->pinentries));
} }
$result = $this->exec(['--decrypt','--skip-verify']); $result = $this->exec(['--decrypt','--skip-verify']);
@ -244,8 +244,8 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
if (!$keys) { if (!$keys) {
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId); throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
} }
if ($private && $this->passphrases) { if ($private && $this->pinentries) {
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->passphrases)); $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->pinentries));
} }
$result = $this->exec([ $result = $this->exec([
$private ? '--export-secret-keys' : '--export', $private ? '--export-secret-keys' : '--export',
@ -263,7 +263,7 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
// \SnappyMail\Log::debug('GnuPG', "export({$fingerprint}, {$passphrase})"); // \SnappyMail\Log::debug('GnuPG', "export({$fingerprint}, {$passphrase})");
if (null !== $passphrase) { if (null !== $passphrase) {
return $this return $this
->addPassphrase($fingerprint, $passphrase) ->addPinentry($fingerprint, $passphrase)
->_exportKey($fingerprint, true); ->_exportKey($fingerprint, true);
} }
return $this->_exportKey($fingerprint); return $this->_exportKey($fingerprint);
@ -365,8 +365,8 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
{ {
$arguments = ['--import']; $arguments = ['--import'];
if ($this->passphrases) { if ($this->pinentries) {
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->passphrases)); $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->pinentries));
} else { } else {
$arguments[] = '--batch'; $arguments[] = '--batch';
} }
@ -618,7 +618,7 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
protected function _sign(/*string|resource*/ $input, /*string|resource*/ $output = null, bool $textmode = true) /*: string|false*/ protected function _sign(/*string|resource*/ $input, /*string|resource*/ $output = null, bool $textmode = true) /*: string|false*/
{ {
if (empty($this->signKeys)) { if (empty($this->pinentries)) {
throw new \Exception('No signing keys specified.'); throw new \Exception('No signing keys specified.');
} }
@ -649,11 +649,11 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
$arguments[] = '--textmode'; $arguments[] = '--textmode';
} }
if ($this->signKeys) { if ($this->pinentries) {
foreach ($this->signKeys as $fingerprint => $pass) { foreach ($this->pinentries as $fingerprint => $pass) {
$arguments[] = '--local-user ' . \escapeshellarg($fingerprint); $arguments[] = '--local-user ' . \escapeshellarg($fingerprint);
} }
$_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->signKeys)); $_ENV['PINENTRY_USER_DATA'] = \json_encode(\array_map('strval', $this->pinentries));
} }
$result = $this->exec($arguments); $result = $this->exec($arguments);

View file

@ -97,8 +97,8 @@ class SMIME extends Base
$fclose = $this->setOutput($output); $fclose = $this->setOutput($output);
if ($this->decryptKeys) { if ($this->pinentries) {
$_ENV['PINENTRY_USER_DATA'] = \json_encode($this->decryptKeys); $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->pinentries);
} }
$result = $this->exec(['--decrypt','--skip-verify']); $result = $this->exec(['--decrypt','--skip-verify']);
@ -243,8 +243,8 @@ class SMIME extends Base
if (!$keys) { if (!$keys) {
throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId); throw new \Exception(($private ? 'Private' : 'Public') . ' key not found: ' . $keyId);
} }
if ($private && $this->passphrases) { if ($private && $this->pinentries) {
$_ENV['PINENTRY_USER_DATA'] = \json_encode($this->passphrases); $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->pinentries);
} }
$result = $this->exec([ $result = $this->exec([
$private ? '--export-secret-key-p12' : '--export', $private ? '--export-secret-key-p12' : '--export',
@ -261,7 +261,7 @@ class SMIME extends Base
{ {
if ($passphrase) { if ($passphrase) {
return $this return $this
->addPassphrase($fingerprint, $passphrase) ->addPinentry($fingerprint, $passphrase)
->_exportKey($fingerprint, true); ->_exportKey($fingerprint, true);
} }
return $this->_exportKey($fingerprint); return $this->_exportKey($fingerprint);
@ -271,8 +271,8 @@ class SMIME extends Base
{ {
$arguments = ['--import']; $arguments = ['--import'];
if ($this->passphrases) { if ($this->pinentries) {
$_ENV['PINENTRY_USER_DATA'] = \json_encode($this->passphrases); $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->pinentries);
} else { } else {
$arguments[] = '--batch'; $arguments[] = '--batch';
} }
@ -482,7 +482,7 @@ class SMIME extends Base
protected function _sign(/*string|resource*/ $input, /*string|resource*/ $output = null) /*: string|false*/ protected function _sign(/*string|resource*/ $input, /*string|resource*/ $output = null) /*: string|false*/
{ {
if (empty($this->signKeys)) { if (empty($this->pinentries)) {
throw new \Exception('No signing keys specified.'); throw new \Exception('No signing keys specified.');
} }
@ -498,11 +498,11 @@ class SMIME extends Base
$arguments[] = '--armor'; $arguments[] = '--armor';
} }
if ($this->signKeys) { if ($this->pinentries) {
foreach ($this->signKeys as $fingerprint => $pass) { foreach ($this->pinentries as $fingerprint => $pass) {
$arguments[] = '--local-user ' . \escapeshellarg($fingerprint); $arguments[] = '--local-user ' . \escapeshellarg($fingerprint);
} }
$_ENV['PINENTRY_USER_DATA'] = \json_encode($this->signKeys); $_ENV['PINENTRY_USER_DATA'] = \json_encode($this->pinentries);
} }
$result = $this->exec($arguments); $result = $this->exec($arguments);

View file

@ -7,6 +7,7 @@ use SnappyMail\SensitiveString;
interface PGPInterface interface PGPInterface
{ {
public static function isSupported() : bool; public static function isSupported() : bool;
// public function addPinentry(string $keyId, SensitiveString $passphrase) : bool;
public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool; public function addDecryptKey(string $fingerprint, SensitiveString $passphrase) : bool;
public function addEncryptKey(string $fingerprint) : bool; public function addEncryptKey(string $fingerprint) : bool;
public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool; public function addSignKey(string $fingerprint, SensitiveString $passphrase) : bool;